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 instanceof List)
037                sb.append(serializeList((List) element));
038            else if (element.getClass().isArray())
039                sb.append(serializeArray(element));
040            else if (element instanceof LinkedHashMap)
041                sb.append(serialize(BeanUtil.bean2Map(element))).append(",");
042            else
043                appendValue(sb, element);
044        }
045        String result = sb.toString();
046        if (result.endsWith(","))
047            result = result.substring(0, result.length() - 1);
048        return result + "]";
049    }
050
051    public String serialize(Map map) {
052        check(map);
053        StringBuilder sb = new StringBuilder("{");
054        for (Object k : map.keySet()) {
055            String key = (String) k;
056            if (ignoreId && "id".equals(key)) {
057                continue;
058            }
059            Object val = map.get(key);
060            if (val instanceof List)
061                sb.append("\"").append(key).append("\":[").append(serializeList((List) val)).append("],");
062            else if (val.getClass().isArray())
063                sb.append("\"").append(key).append("\":[").append(serializeArray(val)).append("],");
064            else if (val instanceof LinkedHashMap)
065                sb.append("\"").append(key).append("\":").append(serialize(BeanUtil.bean2Map(val))).append(",");
066            else
067                appendEntry(sb, key, val);
068        }
069        String result = sb.toString();
070        if (result.endsWith(","))
071            result = result.substring(0, result.length() - 1);
072        return result + "}";
073    }
074
075    private String serializeList(List list) {
076        StringBuilder sb = new StringBuilder();
077        int n = list.size();
078        for (int i = 0; i < n; i++) {
079            Object obj = list.get(i);
080            if (obj instanceof String)
081                sb.append("\"").append(escape((String) obj)).append("\"");
082            else
083                sb.append(serialize(BeanUtil.bean2Map(obj)));
084            if (i < n - 1)
085                sb.append(",");
086        }
087        return sb.toString();
088    }
089
090    private String serializeArray(Object array) {
091        int n = Array.getLength(array);
092        StringBuilder sb = new StringBuilder();
093        for (int i = 0; i < n; i++) {
094            Object obj = Array.get(array, i);
095            if (obj instanceof String)
096                sb.append("\"").append(escape((String) obj)).append("\"");
097            else
098                sb.append(serialize(BeanUtil.bean2Map(obj)));
099            if (i < n - 1)
100                sb.append(",");
101        }
102        return sb.toString();
103    }
104
105    private void appendEntry(StringBuilder sb, String key, Object val) {
106        boolean quote = false;
107        if (!(val instanceof String || val instanceof Number || val instanceof Boolean))
108            val = serialize(BeanUtil.bean2Map(val));
109        else if (val instanceof String) {
110            val = escape((String) val);
111            quote = true;
112        }
113        sb.append("\"").append(key).append("\":").append(quote ? "\"" : "").append(val).append(quote ? "\"" : "")
114                .append(",");
115    }
116
117    private void appendValue(StringBuilder sb, Object val) {
118        boolean quote = false;
119        if (!(val instanceof String || val instanceof Integer || val instanceof Long || 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(quote ? "\"" : "").append(val).append(quote ? "\"" : "").append(",");
126    }
127
128    private static String escape(String str) {
129        StringBuilder sb = new StringBuilder();
130        StringCharacterIterator it = new StringCharacterIterator(str);
131        char ch = it.current();
132        while (ch != StringCharacterIterator.DONE) {
133            switch (ch) {
134                case '"':
135                    sb.append("\\\"");
136                    break;
137                case '\t':
138                    sb.append("\\t");
139                    break;
140                case '\f':
141                    sb.append("\\f");
142                    break;
143                case '\n':
144                    sb.append("\\n");
145                    break;
146                case '\r':
147                    sb.append("\\r");
148                    break;
149                case '\\':
150                    sb.append("\\\\");
151                    break;
152                case '/':
153                    sb.append("\\/");
154                    break;
155                case '\b':
156                    sb.append("\\b");
157                    break;
158                default:
159                    sb.append(ch);
160            }
161            ch = it.next();
162        }
163        return sb.toString();
164    }
165}