View Javadoc

1   package org.apache.struts.flow.json;
2   
3   // java util imports:
4   import java.util.ArrayList;
5   import java.util.Collection;
6   import java.util.Iterator;
7   import java.util.Locale;
8   import java.util.Map;
9   import java.util.*;
10  
11  import java.text.ParseException;
12  import org.apache.commons.logging.*;
13  
14  import java.beans.BeanInfo;
15  import java.beans.Introspector;
16  import java.beans.PropertyDescriptor;
17  import java.lang.reflect.Array;
18  import java.lang.reflect.Method;
19  
20  /***
21   *  Serializes objects into JSON
22   */
23  public class JSONSerializer {
24  
25      private static final Hashtable propDescs = new Hashtable();
26      
27      private Properties varLookup = new Properties();
28      private final static Log log = LogFactory.getLog(JSONSerializer.class);
29  
30      public void setVariableLookupTable(Properties props) {
31         this.varLookup = props;
32         Map.Entry entry;
33         Map map = new HashMap();
34         for (Iterator i = props.entrySet().iterator(); i.hasNext(); ) {
35             entry = (Map.Entry)i.next();
36             map.put(entry.getValue(), entry.getKey());
37         }
38         props.putAll(map);
39      }
40      
41      public String serialize(Object  bean) {
42          Object o = serialize(bean, null, null, new ArrayList());
43          if (o != null) {
44              return o.toString();
45          } 
46          return null;
47      }
48      
49      /***
50       *  Recursive function to serialize objects to JSON objects that will
51       *  handle serialization into JSON. Currently it will
52       *  serialize Collections, Maps, Arrays, and javabeans. It maintains a 
53       *  stack of objects serialized already in the
54       *  current functioncall. This is used to avoid looping (stack overflow) of
55       *  circular linked objects.
56       *
57       *@param  bean              The object you want serialized.
58       *@param  name              The name of the object, used for element
59       *      <name/>
60       *@param  handler           XMLConsumer
61       *@param  stack             Vector of objects we're serializing since the
62       *      first calling of this function (to prevent looping on circular
63       *      references).
64       *@exception  SAXException  If something goes wrong
65       */
66       private Object serialize(Object bean, String name, Object parent, List stack) { 
67          // Check stack for this object
68          //if ((bean != null) && (stack.contains(bean))) {
69              //if (log.isInfoEnabled()) {
70              //    log.info("Circular reference detected, not serializing object: " + name);
71              //}
72          //    return null;
73          //}
74          //else 
75          if (bean != null) {
76              // Push object onto stack.
77              // Don't push null objects ( handled below)
78              stack.add(bean);
79          } else {
80              // bean is null
81              return null;
82          }    
83          String clsName = bean.getClass().getName();
84          Object jsonobject = null;
85  
86          // It depends on the object and it's value what todo next:
87          if (bean instanceof Map) {
88              
89              Map map = (Map) bean;
90              JSONObject object = createObject(parent, name);
91  
92              // Loop through keys and call ourselves
93              for (Iterator i = map.keySet().iterator(); i.hasNext(); ) {
94                  Object key = i.next();
95                  Object Objvalue = map.get(key);
96  
97                  serialize(Objvalue, key.toString(), object, stack);
98              }
99              jsonobject = object;
100         } else if (bean instanceof Collection) {
101             Collection col = (Collection) bean;
102 
103             JSONArray array = createArray(parent, name);
104             // Iterate through components, and call ourselves to process elements
105             for (Iterator i = col.iterator(); i.hasNext(); ) {
106                 serialize(i.next(), null, array, stack);
107             }
108             jsonobject = array;
109         } else if (bean.getClass().isArray()) {
110             JSONArray array = createArray(parent, name);
111             // It's an array, loop through it and keep calling ourselves
112             for (int i = 0; i < Array.getLength(bean); i++) {
113                 serialize(Array.get(bean, i), null, array, stack);
114             }
115             jsonobject = array;
116         }
117         else if (clsName.startsWith("java.lang")) {
118             String val = bean.toString();
119             if (parent != null) {
120                 if (name == null) {
121                     ((JSONArray)parent).put(val);
122                 } else {
123                     name = varLookup.getProperty(name, name);
124                     ((JSONObject)parent).put(name, val);
125                 }
126             }
127             jsonobject = val;
128         } else {
129             // Not java.lang, so we can call ourselves with this object's values
130             try {
131                 PropertyDescriptor[] props = (PropertyDescriptor[])propDescs.get(bean.getClass());
132                 if (props == null) {
133                     BeanInfo info = Introspector.getBeanInfo(bean.getClass());
134                     props = info.getPropertyDescriptors();
135                     propDescs.put(bean.getClass(), props);
136                 }
137                 JSONObject object = createObject(parent, name);
138                 
139 
140                 for (int i = 0; i < props.length; i++) {
141                     Class t = props[i].getPropertyType();
142                     String n = props[i].getName();
143                     Method m = props[i].getReadMethod();
144                     
145                     //System.out.println("read method:"+m);
146                     
147                     // Call ourselves with the result of the method invocation
148                     if (m != null && !m.getName().equals("getClass")) {
149                         serialize(m.invoke(bean, null), n, object, stack);
150                     }
151                 }
152                 jsonobject = object;
153             }
154             catch (Exception e) {
155                 //log.error(e, e);
156                 e.printStackTrace();
157                 //throw new SAXException(e.getMessage());
158             }
159         }
160 
161         // Remove object from stack
162         stack.remove(bean);
163         return jsonobject;
164     }
165     
166     private JSONArray createArray(Object parent, String name) {
167         JSONArray array = new JSONArray();
168         if (parent != null) {
169             if (parent instanceof JSONArray) {
170                 ((JSONArray)parent).put(array);
171             } else {
172                 name = varLookup.getProperty(name, name);
173                 ((JSONObject)parent).put(name, array);
174             }
175         } 
176         return array;
177     }
178      
179     private JSONObject createObject(Object parent, String name) {
180         JSONObject array = new JSONObject();
181         if (parent != null) {
182             if (parent instanceof JSONArray) {
183                 ((JSONArray)parent).put(array);
184             } else {
185                 name = varLookup.getProperty(name, name);
186                 ((JSONObject)parent).put(name, array);
187             }
188         }
189         return array;
190     }
191     
192 }
193