View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.module.json;
8   
9   import java.io.IOException;
10  import java.io.InputStream;
11  import java.io.Reader;
12  import java.io.Serializable;
13  import java.io.StringReader;
14  import java.net.URL;
15  import java.util.ArrayList;
16  import java.util.Iterator;
17  import java.util.List;
18  
19  import org.codehaus.jackson.JsonNode;
20  import org.codehaus.jackson.map.ObjectMapper;
21  
22  /**
23   * A wrapper for the {@link org.codehaus.jackson.JsonNode} object that
24   * allows for nested object keys i.e. user/name will return the name property on
25   * the user object.
26   * <p/>
27   * There is no 'xpath' for JSON yet (though I expect Jackson to do implement this at some point).  
28   * This class provides a simple way to navigate a Json data structure. To select a child entry use -
29   * <code>
30   * person/name
31   * </code>
32   * <p/>
33   * to access array data, use square braces with an index value i.e.
34   * <code>
35   * person/addresses[0]/postcode
36   * <p/>
37   * or
38   * <p/>
39   * [0]/arrayElement
40   * </code>
41   * <p/>
42   * Also, multi-dimensional arrays can be accessed using:
43   * <code>
44   * filters[1]/init[1][0]
45   * </code>
46   * <p/>
47   * if a Json property name contains a '/' the name needs to be quoted with single quotes i.e.
48   * <p/>
49   * <code>
50   * results/'http://foo.com'/value
51   * </code>
52   */
53  public class JsonData implements Serializable
54  {
55      private JsonNode node;
56  
57      public JsonData(JsonNode node)
58      {
59          this.node = node;
60      }
61  
62      public JsonData(URL node) throws IOException
63      {
64          this(node.openStream());
65      }
66  
67      public JsonData(InputStream node) throws IOException
68      {
69          this.node = new ObjectMapper().readTree(node);
70      }
71  
72      public JsonData(Reader node) throws IOException
73      {
74          this.node = new ObjectMapper().readTree(node);
75      }
76  
77      public JsonData(String node) throws IOException
78      {
79          this(new StringReader(node));
80      }
81  
82      public JsonNode get(int index)
83      {
84          return node.get(index);
85      }
86  
87      public boolean isArray()
88      {
89          return node.isArray();
90      }
91      
92      public JsonNode[] toArray() 
93      {
94          List<JsonNode> children = new ArrayList<JsonNode>();
95          for (Iterator<JsonNode> itr = node.getElements(); itr.hasNext();) 
96          {
97              children.add(itr.next());
98          }
99          return children.toArray(new JsonNode[children.size()]);
100     }
101 
102     public JsonNode get(String expression)
103     {
104         List<String> tokens = parseTokens(expression);
105 
106         JsonNode o = node;
107         for (String token : tokens)
108         {
109             if (token.startsWith("["))
110             {
111                 if (o.isArray())
112                 {
113                     int index = Integer.valueOf(token.substring(1, token.length() - 1));
114                     o = o.path(index);
115                 }
116                 else
117                 {
118                     throw new IllegalArgumentException("Current node is not an array, but expression is expecting one");
119                 }
120             }
121             else
122             {
123                 o = o.path(token);
124             }
125 
126             if (o.isMissingNode())
127             {
128                 throw new IllegalArgumentException("Not a valid element: " + token);
129             }
130         }
131 
132         return o;
133     }
134     
135     public String getAsString(String expression)
136     {
137         JsonNode node = get(expression);
138         if (node.isValueNode())
139         {
140             return node.getValueAsText();
141         }
142         else
143         {
144             return node.toString();
145         }
146     }
147     
148     public boolean hasNode(String key)
149     {
150         JsonNode result = node.path(key);
151         return result.isMissingNode() == false;
152     }
153 
154     protected List<String> parseTokens(String expresion)
155     {
156         List<String> tokens = new ArrayList<String>();
157         while (expresion.length() > 0)
158         {
159             int slash = expresion.indexOf("/");
160             int brace = expresion.indexOf("[");
161             //Handled quoted strings
162             if (expresion.charAt(0) == '\'')
163             {
164                 if (expresion.endsWith("'"))
165                 {
166                     tokens.add(expresion.substring(1, expresion.length() - 1));
167                     expresion = "";
168                 }
169                 else
170                 {
171                     int x = expresion.indexOf("'/");
172                     tokens.add(expresion.substring(1, x));
173                     expresion = expresion.substring(x + 2);
174                 }
175             }
176             else if (slash == -1 && brace == -1)
177             {
178                 tokens.add(expresion);
179                 expresion = "";
180             }
181             else if ((slash > -1 && slash < brace) || brace == -1)
182             {
183                 if (slash == 0)
184                 {
185                     if (expresion.charAt(1) == '\'')
186                     {
187                         int x = expresion.indexOf("'", 2);
188                         tokens.add(expresion.substring(2, x));
189                         expresion = expresion.substring(x + 1);
190                     }
191                     else if (expresion.charAt(1) == '[')
192                     {
193                         int i = expresion.indexOf("]", 1);
194                         tokens.add(expresion.substring(0, i));
195                         expresion = expresion.substring(i + 1);
196                     }
197                     else
198                     {
199                         expresion = expresion.substring(slash + 1);
200                     }
201                 }
202                 else
203                 {
204                     tokens.add(expresion.substring(0, slash));
205                     expresion = expresion.substring(slash + 1);
206                 }
207             }
208             else if (brace > 0)
209             {
210                 tokens.add(expresion.substring(0, brace));
211                 expresion = expresion.substring(brace);
212 
213             }
214             else
215             {
216                 int i = expresion.indexOf("]", brace);
217                 tokens.add(expresion.substring(0, i + 1));
218                 expresion = expresion.substring(i + 1);
219             }
220         }
221         return tokens;
222     }
223 
224     @Override
225     public boolean equals(Object obj)
226     {
227         return node.equals(obj);
228     }
229     
230     @Override
231     public int hashCode()
232     {
233         return node.hashCode();
234     }
235 
236     @Override
237     public String toString()
238     {
239         return node.toString();
240     }
241 }