1 package net.minidev.json.reader;
2
3 import java.io.IOException;
4 import java.math.BigDecimal;
5 import java.math.BigInteger;
6 import java.util.Date;
7 import java.util.LinkedList;
8 import java.util.Map;
9 import java.util.concurrent.ConcurrentHashMap;
10 import net.minidev.json.JSONAware;
11 import net.minidev.json.JSONAwareEx;
12 import net.minidev.json.JSONStreamAware;
13 import net.minidev.json.JSONStreamAwareEx;
14 import net.minidev.json.JSONStyle;
15 import net.minidev.json.JSONValue;
16
17 public class JsonWriter {
18     private ConcurrentHashMap<Class<?>, JsonWriterI<?>> data;
19     private LinkedList<WriterByInterface> writerInterfaces;
20
21     public JsonWriter() {
22         data = new ConcurrentHashMap<Class<?>, JsonWriterI<?>>();
23         writerInterfaces = new LinkedList<WriterByInterface>();
24         init();
25     }
26
27     /**
28      * remap field name in custom classes
29      * 
30      * @param fromJava
31      *            field name in java
32      * @param toJson
33      *            field name in json
34      * @since 2.1.1
35      */

36     @SuppressWarnings({ "rawtypes""unchecked" })
37     public <T> void remapField(Class<T> type, String fromJava, String toJson) {
38         JsonWriterI map = this.getWrite(type);
39         if (!(map instanceof BeansWriterASMRemap)) {
40             map = new BeansWriterASMRemap();
41             registerWriter(map, type);
42         }
43         ((BeansWriterASMRemap) map).renameField(fromJava, toJson);
44     }
45
46     static class WriterByInterface {
47         public Class<?> _interface;
48         public JsonWriterI<?> _writer;
49
50         public WriterByInterface(Class<?> _interface, JsonWriterI<?> _writer) {
51             this._interface = _interface;
52             this._writer = _writer;
53         }
54     }
55     
56     /**
57      * try to find a Writer by Cheking implemented interface
58      * @param clazz class to serialize
59      * @return a Writer or null
60      */

61     @SuppressWarnings("rawtypes")
62     public JsonWriterI getWriterByInterface(Class<?> clazz) {
63         for (WriterByInterface w : writerInterfaces) {
64             if (w._interface.isAssignableFrom(clazz))
65                 return w._writer;
66         }
67         return null;
68     }
69
70     @SuppressWarnings("rawtypes")
71     public JsonWriterI getWrite(Class cls) {
72         return data.get(cls);
73     }
74
75     final static public JsonWriterI<JSONStreamAwareEx> JSONStreamAwareWriter = new JsonWriterI<JSONStreamAwareEx>() {
76         public <E extends JSONStreamAwareEx> void writeJSONString(E value, Appendable out, JSONStyle compression) throws IOException {
77             value.writeJSONString(out);
78         }
79     };
80
81     final static public JsonWriterI<JSONStreamAwareEx> JSONStreamAwareExWriter = new JsonWriterI<JSONStreamAwareEx>() {
82         public <E extends JSONStreamAwareEx> void writeJSONString(E value, Appendable out, JSONStyle compression) throws IOException {
83             value.writeJSONString(out, compression);
84         }
85     };
86
87     final static public JsonWriterI<JSONAwareEx> JSONJSONAwareExWriter = new JsonWriterI<JSONAwareEx>() {
88         public <E extends JSONAwareEx> void writeJSONString(E value, Appendable out, JSONStyle compression) throws IOException {
89             out.append(value.toJSONString(compression));
90         }
91     };
92
93     final static public JsonWriterI<JSONAware> JSONJSONAwareWriter = new JsonWriterI<JSONAware>() {
94         public <E extends JSONAware> void writeJSONString(E value, Appendable out, JSONStyle compression) throws IOException {
95             out.append(value.toJSONString());
96         }
97     };
98
99     final static public JsonWriterI<Iterable<? extends Object>> JSONIterableWriter = new JsonWriterI<Iterable<? extends Object>>() {
100         public <E extends Iterable<? extends Object>> void writeJSONString(E list, Appendable out, JSONStyle compression) throws IOException {
101             boolean first = true;
102             compression.arrayStart(out);
103             for (Object value : list) {
104                 if (first) {
105                     first = false;
106                     compression.arrayfirstObject(out);
107                 } else {
108                     compression.arrayNextElm(out);
109                 }
110                 if (value == null)
111                     out.append("null");
112                 else
113                     JSONValue.writeJSONString(value, out, compression);
114                 compression.arrayObjectEnd(out);
115             }
116             compression.arrayStop(out);
117         }
118     };
119
120     final static public JsonWriterI<Enum<?>> EnumWriter = new JsonWriterI<Enum<?>>() {
121         public <E extends Enum<?>> void writeJSONString(E value, Appendable out, JSONStyle compression) throws IOException {
122             @SuppressWarnings("rawtypes")
123             String s = ((Enum) value).name();
124             compression.writeString(out, s);
125         }
126     };
127
128     final static public JsonWriterI<Map<String, ? extends Object>> JSONMapWriter = new JsonWriterI<Map<String, ? extends Object>>() {
129         public <E extends Map<String, ? extends Object>> void writeJSONString(E map, Appendable out, JSONStyle compression) throws IOException {
130             boolean first = true;
131             compression.objectStart(out);
132             /**
133              * do not use <String, Object> to handle non String key maps
134              */

135             for (Map.Entry<?, ?> entry : map.entrySet()) {
136                 Object v = entry.getValue();
137                 if (v == null && compression.ignoreNull())
138                     continue;
139                 if (first) {
140                     compression.objectFirstStart(out);
141                     first = false;
142                 } else {
143                     compression.objectNext(out);
144                 }
145                 JsonWriter.writeJSONKV(entry.getKey().toString(), v, out, compression);
146                 // compression.objectElmStop(out);
147             }
148             compression.objectStop(out);
149         }
150     };
151
152     /**
153      * Json-Smart V2 Beans serialiser
154      * 
155      * Based on ASM
156      */

157     final static public JsonWriterI<Object> beansWriterASM = new BeansWriterASM();
158
159     /**
160      * Json-Smart V1 Beans serialiser
161      */

162     final static public JsonWriterI<Object> beansWriter = new BeansWriter();
163     
164     /**
165      * Json-Smart ArrayWriterClass
166      */

167     final static public JsonWriterI<Object> arrayWriter = new ArrayWriter();
168     
169     /**
170      * ToString Writer
171      */

172     final static public JsonWriterI<Object> toStringWriter = new JsonWriterI<Object>() {
173         public void writeJSONString(Object value, Appendable out, JSONStyle compression) throws IOException {
174             out.append(value.toString());
175         }
176     };
177     
178     public void init() {
179         registerWriter(new JsonWriterI<String>() {
180             public void writeJSONString(String value, Appendable out, JSONStyle compression) throws IOException {
181                 compression.writeString(out, (String) value);
182             }
183         }, String.class);
184
185         registerWriter(new JsonWriterI<Double>() {
186             public void writeJSONString(Double value, Appendable out, JSONStyle compression) throws IOException {
187                 if (value.isInfinite())
188                     out.append("null");
189                 else
190                     out.append(value.toString());
191             }
192         }, Double.class);
193
194         registerWriter(new JsonWriterI<Date>() {
195             public void writeJSONString(Date value, Appendable out, JSONStyle compression) throws IOException {
196                 out.append('"');
197                 JSONValue.escape(value.toString(), out, compression);
198                 out.append('"');
199             }
200         }, Date.class);
201
202         registerWriter(new JsonWriterI<Float>() {
203             public void writeJSONString(Float value, Appendable out, JSONStyle compression) throws IOException {
204                 if (value.isInfinite())
205                     out.append("null");
206                 else
207                     out.append(value.toString());
208             }
209         }, Float.class);
210
211         registerWriter(toStringWriter, Integer.class, Long.class, Byte.class, Short.class, BigInteger.class, BigDecimal.class);
212         registerWriter(toStringWriter, Boolean.class);
213
214         /**
215          * Array
216          */

217
218         registerWriter(new JsonWriterI<int[]>() {
219             public void writeJSONString(int[] value, Appendable out, JSONStyle compression) throws IOException {
220                 boolean needSep = false;
221                 compression.arrayStart(out);
222                 for (int b : value) {
223                     if (needSep)
224                         compression.objectNext(out);
225                     else
226                         needSep = true;
227                     out.append(Integer.toString(b));
228                 }
229                 compression.arrayStop(out);
230             }
231         }, int[].class);
232
233         registerWriter(new JsonWriterI<short[]>() {
234             public void writeJSONString(short[] value, Appendable out, JSONStyle compression) throws IOException {
235                 boolean needSep = false;
236                 compression.arrayStart(out);
237                 for (short b : value) {
238                     if (needSep)
239                         compression.objectNext(out);
240                     else
241                         needSep = true;
242                     out.append(Short.toString(b));
243                 }
244                 compression.arrayStop(out);
245             }
246         }, short[].class);
247
248         registerWriter(new JsonWriterI<long[]>() {
249             public void writeJSONString(long[] value, Appendable out, JSONStyle compression) throws IOException {
250                 boolean needSep = false;
251                 compression.arrayStart(out);
252                 for (long b : value) {
253                     if (needSep)
254                         compression.objectNext(out);
255                     else
256                         needSep = true;
257                     out.append(Long.toString(b));
258                 }
259                 compression.arrayStop(out);
260             }
261         }, long[].class);
262
263         registerWriter(new JsonWriterI<float[]>() {
264             public void writeJSONString(float[] value, Appendable out, JSONStyle compression) throws IOException {
265                 boolean needSep = false;
266                 compression.arrayStart(out);
267                 for (float b : value) {
268                     if (needSep)
269                         compression.objectNext(out);
270                     else
271                         needSep = true;
272                     out.append(Float.toString(b));
273                 }
274                 compression.arrayStop(out);
275             }
276         }, float[].class);
277
278         registerWriter(new JsonWriterI<double[]>() {
279             public void writeJSONString(double[] value, Appendable out, JSONStyle compression) throws IOException {
280                 boolean needSep = false;
281                 compression.arrayStart(out);
282                 for (double b : value) {
283                     if (needSep)
284                         compression.objectNext(out);
285                     else
286                         needSep = true;
287                     out.append(Double.toString(b));
288                 }
289                 compression.arrayStop(out);
290             }
291         }, double[].class);
292
293         registerWriter(new JsonWriterI<boolean[]>() {
294             public void writeJSONString(boolean[] value, Appendable out, JSONStyle compression) throws IOException {
295                 boolean needSep = false;
296                 compression.arrayStart(out);
297                 for (boolean b : value) {
298                     if (needSep)
299                         compression.objectNext(out);
300                     else
301                         needSep = true;
302                     out.append(Boolean.toString(b));
303                 }
304                 compression.arrayStop(out);
305             }
306         }, boolean[].class);
307
308         registerWriterInterface(JSONStreamAwareEx.class, JsonWriter.JSONStreamAwareExWriter);
309         registerWriterInterface(JSONStreamAware.class, JsonWriter.JSONStreamAwareWriter);
310         registerWriterInterface(JSONAwareEx.class, JsonWriter.JSONJSONAwareExWriter);
311         registerWriterInterface(JSONAware.class, JsonWriter.JSONJSONAwareWriter);
312         registerWriterInterface(Map.class, JsonWriter.JSONMapWriter);
313         registerWriterInterface(Iterable.class, JsonWriter.JSONIterableWriter);
314         registerWriterInterface(Enum.class, JsonWriter.EnumWriter);
315         registerWriterInterface(Number.class, JsonWriter.toStringWriter);
316     }
317
318     /**
319      * associate an Writer to a interface With Hi priority
320      * @param interFace interface to map
321      * @param writer writer Object
322      * @deprecated use registerWriterInterfaceFirst
323      */

324     public void addInterfaceWriterFirst(Class<?> interFace, JsonWriterI<?> writer) {
325         registerWriterInterfaceFirst(interFace, writer);
326     }
327
328     /**
329      * associate an Writer to a interface With Low priority
330      * @param interFace interface to map
331      * @param writer writer Object
332      * @deprecated use registerWriterInterfaceLast
333      */

334     public void addInterfaceWriterLast(Class<?> interFace, JsonWriterI<?> writer) {
335         registerWriterInterfaceLast(interFace, writer);
336     }
337
338     /**
339      * associate an Writer to a interface With Low priority
340      * @param interFace interface to map
341      * @param writer writer Object
342      */

343     public void registerWriterInterfaceLast(Class<?> interFace, JsonWriterI<?> writer) {
344         writerInterfaces.addLast(new WriterByInterface(interFace, writer));
345     }
346     
347     /**
348      * associate an Writer to a interface With Hi priority
349      * @param interFace interface to map
350      * @param writer writer Object
351      */

352     public void registerWriterInterfaceFirst(Class<?> interFace, JsonWriterI<?> writer) {
353         writerInterfaces.addFirst(new WriterByInterface(interFace, writer));
354     }
355
356     /**
357      * an alias for registerWriterInterfaceLast
358      * @param interFace interface to map
359      * @param writer writer Object
360      */

361     public void registerWriterInterface(Class<?> interFace, JsonWriterI<?> writer) {
362         registerWriterInterfaceLast(interFace, writer);
363     }
364
365     /**
366      * associate an Writer to a Class
367      * @param writer
368      * @param cls
369      */

370     public <T> void registerWriter(JsonWriterI<T> writer, Class<?>... cls) {
371         for (Class<?> c : cls)
372             data.put(c, writer);
373     }
374
375     /**
376      * Write a Key : value entry to a stream
377      */

378     public static void writeJSONKV(String key, Object value, Appendable out, JSONStyle compression) throws IOException {
379         if (key == null)
380             out.append("null");
381         else if (!compression.mustProtectKey(key))
382             out.append(key);
383         else {
384             out.append('"');
385             JSONValue.escape(key, out, compression);
386             out.append('"');
387         }
388         compression.objectEndOfKey(out);
389         if (value instanceof String) {
390             compression.writeString(out, (String) value);
391         } else
392             JSONValue.writeJSONString(value, out, compression);
393         compression.objectElmStop(out);
394     }
395 }
396