001package gudusoft.gsqlparser.util.json;
002
003import gudusoft.gsqlparser.dlineage.util.BeanUtil;
004
005import java.io.Serializable;
006import java.lang.reflect.Array;
007import java.text.StringCharacterIterator;
008import java.util.LinkedHashMap;
009import java.util.List;
010import java.util.Map;
011
012@SuppressWarnings("rawtypes")
013public class JSONSerializer implements Serializable {
014
015    private static final long serialVersionUID = -947149613761603468L;
016
017    private boolean ignoreId = false;
018
019    public JSONSerializer(boolean ignoreId) {
020        this.ignoreId = ignoreId;
021    }
022
023    public JSONSerializer() {
024    }
025
026    private void check(Object obj) {
027        if (obj == null || !(obj instanceof LinkedHashMap || obj instanceof List)) {
028            throw new IllegalArgumentException("Invalid JSON structure");
029        }
030    }
031
032    public String serialize(List list) {
033        check(list);
034        StringBuilder sb = new StringBuilder("[");
035        for (Object element : list) {
036            if (element == null) {
037                continue; // Skip null values
038            }
039            if (element instanceof List)
040                sb.append(serializeList((List) element));
041            else if (element.getClass().isArray())
042                sb.append(serializeArray(element));
043            else if (element instanceof LinkedHashMap)
044                sb.append(serialize(BeanUtil.bean2Map(element))).append(",");
045            else
046                appendValue(sb, element);
047        }
048        String result = sb.toString();
049        if (result.endsWith(","))
050            result = result.substring(0, result.length() - 1);
051        return result + "]";
052    }
053
054    public String serialize(Map map) {
055        check(map);
056        StringBuilder sb = new StringBuilder("{");
057        for (Object k : map.keySet()) {
058            String key = (String) k;
059            if (ignoreId && ("id".equals(key))) {
060                continue;
061            }
062            Object val = map.get(key);
063            if (val == null) {
064                continue; // Skip null values
065            }
066            if (val instanceof List)
067                sb.append("\"").append(key).append("\":[").append(serializeList((List) val)).append("]").append(",");
068            else if (val.getClass().isArray())
069                sb.append("\"").append(key).append("\":[").append(serializeArray(val)).append("]").append(",");
070            else if (val instanceof LinkedHashMap)
071                sb.append("\"").append(key).append("\":").append(serialize(BeanUtil.bean2Map(val))).append(",");
072            else
073                appendEntry(sb, key, val);
074        }
075        String result = sb.toString();
076        if (result.endsWith(","))
077            result = result.substring(0, result.length() - 1);
078        return result + "}";
079    }
080
081    private String serializeList(List list) {
082        StringBuilder sb = new StringBuilder();
083        int n = list.size();
084        for (int i = 0; i < n; i++) {
085            Object obj = list.get(i);
086            if (obj == null) {
087                continue; // Skip null values
088            }
089            if (obj instanceof String)
090                sb.append("\"").append(escape((String) obj)).append("\"");
091            else
092                sb.append(serialize(BeanUtil.bean2Map(obj)));
093            if (i < n - 1)
094                sb.append(",");
095        }
096        return sb.toString();
097    }
098
099    private String serializeArray(Object array) {
100        int n = Array.getLength(array);
101        StringBuilder sb = new StringBuilder();
102        for (int i = 0; i < n; i++) {
103            Object obj = Array.get(array, i);
104            if (obj == null) {
105                continue; // Skip null values
106            }
107            if (obj instanceof String)
108                sb.append("\"").append(escape((String) obj)).append("\"");
109            else
110                sb.append(serialize(BeanUtil.bean2Map(obj)));
111            if (i < n - 1)
112                sb.append(",");
113        }
114        return sb.toString();
115    }
116
117    private void appendEntry(StringBuilder sb, String key, Object val) {
118        boolean quote = false;
119        if (!(val instanceof String || val instanceof Number || val instanceof Boolean))
120            val = serialize(BeanUtil.bean2Map(val));
121        else if (val instanceof String) {
122            val = escape((String) val);
123            quote = true;
124        }
125        sb.append("\"").append(key).append("\":").append(quote ? "\"" : "").append(val).append(quote ? "\"" : "")
126                .append(",");
127    }
128
129    private void appendValue(StringBuilder sb, Object val) {
130        boolean quote = false;
131        if (!(val instanceof String || val instanceof Integer || val instanceof Long || val instanceof Boolean))
132            val = serialize(BeanUtil.bean2Map(val));
133        else if (val instanceof String) {
134            val = escape((String) val);
135            quote = true;
136        }
137        sb.append(quote ? "\"" : "").append(val).append(quote ? "\"" : "").append(",");
138    }
139
140    private static String escape(String str) {
141        StringBuilder sb = new StringBuilder();
142        StringCharacterIterator it = new StringCharacterIterator(str);
143        char ch = it.current();
144        while (ch != StringCharacterIterator.DONE) {
145            switch (ch) {
146                case '"':
147                    sb.append("\\\"");
148                    break;
149                case '\t':
150                    sb.append("\\t");
151                    break;
152                case '\f':
153                    sb.append("\\f");
154                    break;
155                case '\n':
156                    sb.append("\\n");
157                    break;
158                case '\r':
159                    sb.append("\\r");
160                    break;
161                case '\\':
162                    sb.append("\\\\");
163                    break;
164                case '/':
165                    sb.append("\\/");
166                    break;
167                case '\b':
168                    sb.append("\\b");
169                    break;
170                default:
171                    sb.append(ch);
172            }
173            ch = it.next();
174        }
175        return sb.toString();
176    }
177}