001package gudusoft.gsqlparser.util.json;
002
003import gudusoft.gsqlparser.dlineage.util.BeanUtil;
004
005import java.io.IOException;
006import java.io.Serializable;
007import java.io.Writer;
008import java.lang.reflect.Array;
009import java.text.StringCharacterIterator;
010import java.util.LinkedHashMap;
011import java.util.List;
012import java.util.Map;
013
014@SuppressWarnings("rawtypes")
015public class JSONSerializer implements Serializable {
016
017    private static final long serialVersionUID = -947149613761603468L;
018
019    private boolean ignoreId = false;
020
021    public JSONSerializer(boolean ignoreId) {
022        this.ignoreId = ignoreId;
023    }
024
025    public JSONSerializer() {
026    }
027
028    private void check(Object obj) {
029        if (obj == null || !(obj instanceof LinkedHashMap || obj instanceof List)) {
030            throw new IllegalArgumentException("Invalid JSON structure");
031        }
032    }
033
034    public String serialize(List list) {
035        check(list);
036        StringBuilder sb = new StringBuilder("[");
037        for (Object element : list) {
038            if (element == null) {
039                continue; // Skip null values
040            }
041            if (element instanceof List)
042                sb.append(serializeList((List) element));
043            else if (element.getClass().isArray())
044                sb.append(serializeArray(element));
045            else if (element instanceof LinkedHashMap)
046                sb.append(serialize(BeanUtil.bean2Map(element))).append(",");
047            else
048                appendValue(sb, element);
049        }
050        String result = sb.toString();
051        if (result.endsWith(","))
052            result = result.substring(0, result.length() - 1);
053        return result + "]";
054    }
055
056    public String serialize(Map map) {
057        check(map);
058        StringBuilder sb = new StringBuilder("{");
059        for (Object k : map.keySet()) {
060            String key = (String) k;
061            if (ignoreId && ("id".equals(key))) {
062                continue;
063            }
064            Object val = map.get(key);
065            if (val == null) {
066                continue; // Skip null values
067            }
068            if (val instanceof List)
069                sb.append("\"").append(key).append("\":[").append(serializeList((List) val)).append("]").append(",");
070            else if (val.getClass().isArray())
071                sb.append("\"").append(key).append("\":[").append(serializeArray(val)).append("]").append(",");
072            else if (val instanceof LinkedHashMap)
073                sb.append("\"").append(key).append("\":").append(serialize(BeanUtil.bean2Map(val))).append(",");
074            else
075                appendEntry(sb, key, val);
076        }
077        String result = sb.toString();
078        if (result.endsWith(","))
079            result = result.substring(0, result.length() - 1);
080        return result + "}";
081    }
082
083    public void serialize(Map map, Writer writer) throws IOException {
084        check(map);
085        writer.write("{");
086        boolean first = true;
087        for (Object k : map.keySet()) {
088            String key = (String) k;
089            if (ignoreId && ("id".equals(key))) {
090                continue;
091            }
092            Object val = map.get(key);
093            if (val == null) {
094                continue;
095            }
096            if (!first) {
097                writer.write(",");
098            }
099            first = false;
100            if (val instanceof List) {
101                writer.write("\"" + key + "\":[");
102                serializeList((List) val, writer);
103                writer.write("]");
104            } else if (val.getClass().isArray()) {
105                writer.write("\"" + key + "\":[");
106                serializeArray(val, writer);
107                writer.write("]");
108            } else if (val instanceof LinkedHashMap) {
109                writer.write("\"" + key + "\":");
110                serialize((Map) BeanUtil.bean2Map(val), writer);
111            } else {
112                appendEntry(writer, key, val);
113            }
114        }
115        writer.write("}");
116    }
117
118    public void serialize(List list, Writer writer) throws IOException {
119        check(list);
120        writer.write("[");
121        boolean first = true;
122        for (Object element : list) {
123            if (element == null) {
124                continue;
125            }
126            if (!first) {
127                writer.write(",");
128            }
129            first = false;
130            if (element instanceof List) {
131                serializeList((List) element, writer);
132            } else if (element.getClass().isArray()) {
133                serializeArray(element, writer);
134            } else if (element instanceof LinkedHashMap) {
135                serialize((Map) BeanUtil.bean2Map(element), writer);
136            } else {
137                appendValue(writer, element);
138            }
139        }
140        writer.write("]");
141    }
142
143    private String serializeList(List list) {
144        StringBuilder sb = new StringBuilder();
145        int n = list.size();
146        for (int i = 0; i < n; i++) {
147            Object obj = list.get(i);
148            if (obj == null) {
149                continue; // Skip null values
150            }
151            if (obj instanceof String)
152                sb.append("\"").append(escape((String) obj)).append("\"");
153            else
154                sb.append(serialize(BeanUtil.bean2Map(obj)));
155            if (i < n - 1)
156                sb.append(",");
157        }
158        return sb.toString();
159    }
160
161    private void serializeList(List list, Writer writer) throws IOException {
162        int n = list.size();
163        for (int i = 0; i < n; i++) {
164            Object obj = list.get(i);
165            if (obj == null) {
166                continue;
167            }
168            if (obj instanceof String)
169                writer.write("\"" + escape((String) obj) + "\"");
170            else
171                serialize((Map) BeanUtil.bean2Map(obj), writer);
172            if (i < n - 1)
173                writer.write(",");
174        }
175    }
176
177    private String serializeArray(Object array) {
178        int n = Array.getLength(array);
179        StringBuilder sb = new StringBuilder();
180        for (int i = 0; i < n; i++) {
181            Object obj = Array.get(array, i);
182            if (obj == null) {
183                continue; // Skip null values
184            }
185            if (obj instanceof String)
186                sb.append("\"").append(escape((String) obj)).append("\"");
187            else
188                sb.append(serialize(BeanUtil.bean2Map(obj)));
189            if (i < n - 1)
190                sb.append(",");
191        }
192        return sb.toString();
193    }
194
195    private void serializeArray(Object array, Writer writer) throws IOException {
196        int n = Array.getLength(array);
197        for (int i = 0; i < n; i++) {
198            Object obj = Array.get(array, i);
199            if (obj == null) {
200                continue;
201            }
202            if (obj instanceof String)
203                writer.write("\"" + escape((String) obj) + "\"");
204            else
205                serialize((Map) BeanUtil.bean2Map(obj), writer);
206            if (i < n - 1)
207                writer.write(",");
208        }
209    }
210
211    private void appendEntry(StringBuilder sb, String key, Object val) {
212        boolean quote = false;
213        if (!(val instanceof String || val instanceof Number || val instanceof Boolean))
214            val = serialize(BeanUtil.bean2Map(val));
215        else if (val instanceof String) {
216            val = escape((String) val);
217            quote = true;
218        }
219        sb.append("\"").append(key).append("\":").append(quote ? "\"" : "").append(val).append(quote ? "\"" : "")
220                .append(",");
221    }
222
223    private void appendEntry(Writer writer, String key, Object val) throws IOException {
224        if (!(val instanceof String || val instanceof Number || val instanceof Boolean)) {
225            writer.write("\"" + key + "\":");
226            serialize((Map) BeanUtil.bean2Map(val), writer);
227        } else if (val instanceof String) {
228            writer.write("\"" + key + "\":\"" + escape((String) val) + "\"");
229        } else {
230            writer.write("\"" + key + "\":" + val);
231        }
232    }
233
234    private void appendValue(StringBuilder sb, Object val) {
235        boolean quote = false;
236        if (!(val instanceof String || val instanceof Integer || val instanceof Long || val instanceof Boolean))
237            val = serialize(BeanUtil.bean2Map(val));
238        else if (val instanceof String) {
239            val = escape((String) val);
240            quote = true;
241        }
242        sb.append(quote ? "\"" : "").append(val).append(quote ? "\"" : "").append(",");
243    }
244
245    private void appendValue(Writer writer, Object val) throws IOException {
246        if (!(val instanceof String || val instanceof Integer || val instanceof Long || val instanceof Boolean)) {
247            serialize((Map) BeanUtil.bean2Map(val), writer);
248        } else if (val instanceof String) {
249            writer.write("\"" + escape((String) val) + "\"");
250        } else {
251            writer.write(val.toString());
252        }
253    }
254
255    private static String escape(String str) {
256        StringBuilder sb = new StringBuilder();
257        StringCharacterIterator it = new StringCharacterIterator(str);
258        char ch = it.current();
259        while (ch != StringCharacterIterator.DONE) {
260            switch (ch) {
261                case '"':
262                    sb.append("\\\"");
263                    break;
264                case '\t':
265                    sb.append("\\t");
266                    break;
267                case '\f':
268                    sb.append("\\f");
269                    break;
270                case '\n':
271                    sb.append("\\n");
272                    break;
273                case '\r':
274                    sb.append("\\r");
275                    break;
276                case '\\':
277                    sb.append("\\\\");
278                    break;
279                case '/':
280                    sb.append("\\/");
281                    break;
282                case '\b':
283                    sb.append("\\b");
284                    break;
285                default:
286                    sb.append(ch);
287            }
288            ch = it.next();
289        }
290        return sb.toString();
291    }
292}