My favorites | Sign in
Project Logo
                
Search
for
Updated Mar 18, 2009 by fangyidong
DecodingExamples  
Examples of JSON decoding

Example 1 - Convenient way: Use JSONValue

  System.out.println("=======decode=======");
		
  String s="[0,{\"1\":{\"2\":{\"3\":{\"4\":[5,{\"6\":7}]}}}}]";
  Object obj=JSONValue.parse(s);
  JSONArray array=(JSONArray)obj;
  System.out.println("======the 2nd element of array======");
  System.out.println(array.get(1));
  System.out.println();
		
  JSONObject obj2=(JSONObject)array.get(1);
  System.out.println("======field \"1\"==========");
  System.out.println(obj2.get("1"));	

		
  s="{}";
  obj=JSONValue.parse(s);
  System.out.println(obj);
		
  s="[5,]";
  obj=JSONValue.parse(s);
  System.out.println(obj);
		
  s="[5,,2]";
  obj=JSONValue.parse(s);
  System.out.println(obj);

JSONObject is a java.util.Map and JSONArray is a java.util.List, so you can access them

with standard operations of Map or List. Please refer Mapping Between JSON and Java Entities for more information on entity mapping while parsing.

Example 2 - Faster way: Reuse instance of JSONParser

  JSONParser parser=new JSONParser();

  System.out.println("=======decode=======");
		
  String s="[0,{\"1\":{\"2\":{\"3\":{\"4\":[5,{\"6\":7}]}}}}]";
  Object obj=parser.parse(s);
  JSONArray array=(JSONArray)obj;
  System.out.println("======the 2nd element of array======");
  System.out.println(array.get(1));
  System.out.println();
		
  JSONObject obj2=(JSONObject)array.get(1);
  System.out.println("======field \"1\"==========");
  System.out.println(obj2.get("1"));	

		
  s="{}";
  obj=parser.parse(s);
  System.out.println(obj);
		
  s="[5,]";
  obj=parser.parse(s);
  System.out.println(obj);
		
  s="[5,,2]";
  obj=parser.parse(s);
  System.out.println(obj);

JSONObject is a java.util.Map and JSONArray is a java.util.List, so you can access them with standard operations of Map or List. Please refer Mapping Between JSON and Java Entities for more information on entity mapping while parsing.

Example 3 - Exception handling

  String jsonText = "[[null, 123.45, \"a\\tb c\"]}, true";
  JSONParser parser = new JSONParser();
		
  try{
    parser.parse(jsonText);
  }
  catch(ParseException pe){
    System.out.println("position: " + pe.getPosition());
    System.out.println(pe);
  }

Result:

  position: 25
  Unexpected token RIGHT BRACE(}) at position 25.

Please refer ParseException.java for more information.

Example 4 - Container factory

You can use ContainerFactory to create containers for parsed JSON objects and JSON arrays:

  String jsonText = "{\"first\": 123, \"second\": [4, 5, 6], \"third\": 789}";
  JSONParser parser = new JSONParser();
  ContainerFactory containerFactory = new ContainerFactory(){
    public List creatArrayContainer() {
      return new LinkedList();
    }

    public Map createObjectContainer() {
      return new LinkedHashMap();
    }
			
  };
		
  try{
    Map json = (Map)parser.parse(jsonText, containerFactory);
    Iterator iter = json.entrySet().iterator();
    System.out.println("==iterate result==");
    while(iter.hasNext()){
      Map.Entry entry = (Map.Entry)iter.next();
      System.out.println(entry.getKey() + "=>" + entry.getValue());
    }
			
    System.out.println("==toJSONString()==");
    System.out.println(JSONValue.toJSONString(json));
  }
  catch(ParseException pe){
    System.out.println(pe);
  }

Result:

  ==iterate result==
  first=>123
  second=>[4, 5, 6]
  third=>789
  ==toJSONString()==
  {"first":123,"second":[4,5,6],"third":789}

If you don't specify a container factory, org.json.simple.JSONObject is used for a Map and org.json.simple.JSONArray is used for a List. Please refer Mapping Between JSON and Java Entities for more information on entity mapping while parsing.

Example 5 - Stoppable SAX-like content handler

JSON.simple introduces a simplified and stoppable SAX-like content handler to process JSON text stream. The user can pause at any point of the logical input stream, processing other logic, then resume parsing if needed, or abort parsing if he gets the desired result, without having to wait for the whole input stream to finish. Then we have a very fast parser without sacrificing the flexibility. Here's an example of finding values of any object entry that matches a desired key:

KeyFinder.java:

class KeyFinder implements ContentHandler{
  private Object value;
  private boolean found = false;
  private boolean end = false;
  private String key;
  private String matchKey;
	
  public void setMatchKey(String matchKey){
    this.matchKey = matchKey;
  }
	
  public Object getValue(){
    return value;
  }
	
  public boolean isEnd(){
    return end;
  }
	
  public void setFound(boolean found){
    this.found = found;
  }
	
  public boolean isFound(){
    return found;
  }
	
  public void startJSON() throws ParseException, IOException {
    found = false;
    end = false;
  }

  public void endJSON() throws ParseException, IOException {
    end = true;
  }

  public boolean primitive(Object value) throws ParseException, IOException {
    if(key != null){
      if(key.equals(matchKey)){
        found = true;
	this.value = value;
	key = null;
	return false;
      }
    }
    return true;
  }

  public boolean startArray() throws ParseException, IOException {
    return true;
  }

	
  public boolean startObject() throws ParseException, IOException {
    return true;
  }

  public boolean startObjectEntry(String key) throws ParseException, IOException {
    this.key = key;
    return true;
  }
	
  public boolean endArray() throws ParseException, IOException {
    return false;
  }

  public boolean endObject() throws ParseException, IOException {
    return true;
  }

  public boolean endObjectEntry() throws ParseException, IOException {
    return true;
  }
}

Main logic:

  String jsonText = "{\"first\": 123, \"second\": [{\"k1\":{\"id\":\"id1\"}}, 4, 5, 6, {\"id\": 123}], \"third\": 789, \"id\": null}";
  JSONParser parser = new JSONParser();
  KeyFinder finder = new KeyFinder();
  finder.setMatchKey("id");
  try{
    while(!finder.isEnd()){
      parser.parse(jsonText, finder, true);
      if(finder.isFound()){
        finder.setFound(false);
        System.out.println("found id:");
        System.out.println(finder.getValue());
      }
    }		
  }
  catch(ParseException pe){
    pe.printStackTrace();
  }

Result:

  found id:
  id1
  found id:
  123
  found id:
  null

Please refer ContentHandler.java for more information.

Example 6 - Build whole object graph on top of SAX-like content handler

Please note that JSON.simple has provided the build in functionality to do the same work. Please refer above examples for more information. Here is just an example to show how to use the SAX-like interface in a slightly more complex scenario.

Transformer.java:

class Transformer implements ContentHandler{
        private Stack valueStack;
        
        public Object getResult(){
            if(valueStack == null || valueStack.size() == 0)
                return null;
            return valueStack.peek();
        }
        
        public boolean endArray () throws ParseException, IOException {
            trackBack();
            return true;
        }

        public void endJSON () throws ParseException, IOException {}

        public boolean endObject () throws ParseException, IOException {
            trackBack();
            return true;
        }

        public boolean endObjectEntry () throws ParseException, IOException {
            Object value = valueStack.pop();
            Object key = valueStack.pop();
            Map parent = (Map)valueStack.peek();
            parent.put(key, value);
            return true;
        }

        private void trackBack(){
            if(valueStack.size() > 1){
                Object value = valueStack.pop();
                Object prev = valueStack.peek();
                if(prev instanceof String){
                    valueStack.push(value);
                }
            }
        }
        
        private void consumeValue(Object value){
            if(valueStack.size() == 0)
                valueStack.push(value);
            else{
                Object prev = valueStack.peek();
                if(prev instanceof List){
                    List array = (List)prev;
                    array.add(value);
                }
                else{
                    valueStack.push(value);
                }
            }
        }
        
        public boolean primitive (Object value) throws ParseException, IOException {
            consumeValue(value);
            return true;
        }

        public boolean startArray () throws ParseException, IOException {
            List array = new JSONArray();
            consumeValue(array);
            valueStack.push(array);
            return true;
        }

        public void startJSON () throws ParseException, IOException {
            valueStack = new Stack();
        }

        public boolean startObject () throws ParseException, IOException {
            Map object = new JSONObject();
            consumeValue(object);
            valueStack.push(object);
            return true;
        }

        public boolean startObjectEntry (String key) throws ParseException, IOException {
            valueStack.push(key);
            return true;
        }
        
    }

Main logic:

    String jsonString = <Input JSON text>;
    Object value = null;
    JSONParser parser = new JSONParser();
    Transformer transformer = new Transformer();
        
    parser.parse(jsonString, transformer);
    value = transformer.getResult();

The result is similar to one return from the following code:

    String jsonString = <Input JSON text>;
    Object value = null;
    JSONParser parser = new JSONParser();
    value = parser.parse(jsonString);

Notes - Multithreading and extensions

JSONParser is NOT thread-safe. And please note that JSON string such as [5,,,2] is accepted by the parser and is treated as [5,2]. This doesn't violate the JSON specification, and it increases the error toleration of the input data. Some JSON grammar checker may need a 'strict' mode. Considering adding this feature.

Since it's a bit controversial on the extension of the parser (see comments of this wiki page), I'd like to make some clarifications on this topic here.

Some users may concern about exchanging important data, say medical information or financial data, between applications. I think you need to make sure the following things in such scenarios:

  1. You need to make sure you are using a reliable and full compliant encoder on the side of the source;
  2. Or if you also accept data from a non-trusted source, even a 'strict' decoder is inadequate. You may need extra validation rules to verify the source. For example, a user may send totally compliant [5,2] even though you expect [5,0,2].

In both cases, a liberal parser will do nothing harmful to your application.

The reason of accepting something like [5,,2] is that:

  1. A careless user or an encoder may repeat a comma (such as by pressing the key too long :-), which is harmless;
  2. The comma is actually redundant syntactically, if two adjacent entities are recognized.

I know some users may be FUD in front of a liberal parser, but as I mentioned above, it's harmless and is allowed in RFC4627 (actually the author of RFC4627 adopts this feature in the reference implementation).

Please feel free to leave a comment or post in the discussion group if you have further concerns. Thanks.


Comment by tsaloranta, Jan 07, 2009

Actually, why wouldn't such a non-standard feature violate JSON specification? Such a construct does not conform to the JSON syntax, so by definition it seems to explicitly violate it. And the main problem is that accepting such invalid input reduces interoperability -- other parser will not accept such structures, and it may make users less likely to use json.simple, because its behavior differs from standard behavior.

Comment by fangyidong, Jan 07, 2009

RFC 4627 states:

4. Parsers
A JSON parser transforms a JSON text into another representation. A JSON parser MUST accept all texts that conform to the JSON grammar. A JSON parser MAY accept non-JSON forms or extensions.
Comment by fangyidong, Jan 10, 2009

It does not cause interoperability issues because the encoding is always right, and JSON.simple always accepts inputs that conform to the JSON grammar.

Comment by fangyidong, Jan 12, 2009

Actually, JSON.org's RI goes further on extension. It accepts single quote strings and hex number: : 0x20?.

Comment by tsaloranta, Feb 09, 2009

Yes, RI is non-compliant as well. It is true that specification allows for extensions; unfortunately there are no commonly defined "common" extensions and thus each parser seems to adopt their own favorite hacks.

But it is bit naive to think that accepting non-valid input would not lead to interoperability problems -- this is EXACTLY how browsers degenerated into accepting all kinds of broken html. So personally I think it is wrong to add short-cuts for what are essentially broken documents (missing values, or extra commas).

But it is your parser of course, and you can add any extensions that you like. :-)

Comment by fangyidong, Feb 10, 2009

Please read RFC4627 carefully, then you'll find the lines as below:

5. Generators

   A JSON generator produces JSON text.  The resulting text MUST
   strictly conform to the JSON grammar.

That is, the encoder should be always right, so the interoperability problem will never happen. The extension of the parser is to increase the robustness of the application. Both the RI and JSON.simple are fully compliant with RFC4627. If you insists that there will be interoperability problems, it's the problem of the specification, not the library. But the fact is, it will never happen.

Comment by arnauldvm, Mar 13, 2009

I agree with tsaloranta.

Accepting non-compliant input creates the risk of letting non-compliant content spread (e.g. contents generated by hand, or by another system that has a flaw). As long as the only "client" to these contents is read by a "tolerant" parser you have no problem.

The day another system, using a more strict parser, comes into play, you have your interoperability problem, and it might be too late to come back.

Comment by PeterQuinsey, Mar 15, 2009

arnauldvm, tsaloranta: Please take the time to review Postel's Law: "Be conservative in what you do; be liberal in what you accept from others."

http://en.wikipedia.org/wiki/Postel%27s_law

The responsibility for enforcing valid JSON lies with the encoder, not the decoder. The decoder's sole responsibility is to parse JSON. By gracefully accepting invalid input, the decoder becomes more robust and usable.

Comment by keithdwinkler, Mar 18, 2009

"Be conservative in what you do; be liberal in what you accept from others."

The complication lies in that "accepting" means making assumptions about the intention of the author. Does "5,,,2" mean "5,2", or "5,null,null,2", "5,0,0,2", or something else?

I suspect that if I were parsing, say, important medical data, I would need a strict mode, that would fail in this case, or at least warn somehow.

Comment by fangyidong, Mar 18, 2009

Hi keithdwinkler, if you are exchanging important data, say medical information or financial data, between applications, I think you need to make sure the following things in such scenarios:

  1. You need to make sure you are using a reliable and full compliant encoder on the side of the source;
  2. Or if you also accept data from a non-trusted source, even a 'strict' decoder is inadequate. You may need extra validation rules to verify the source. For example, a user may send totally compliant [5,2] even though you expect [5,0,2].

In both cases, a liberal parser will do nothing harmful to your application.

The reason of accepting something like [5,,2] is that:

  1. A careless user or an encoder may repeat a comma (such as by pressing the key too long :-), which is harmless;
  2. The comma is actually redundant syntactically, if two adjacent entities are recognized.

I know some users may be FUD in front of a liberal parser, but as I mentioned above, it's harmless and is allowed in RFC4627 (actually the author of RFC4627 adopts this feature in the reference implementation).

Please feel free to leave a comment or post in the discussion group if you have further concerns. Thanks.

Comment by eliseosoto, Nov 03, 2009

Any particular reason why JSONValue.parse(s) returns an Object and not a more proper JSONObject?

Comment by fangyidong, Nov 03, 2009

Because we also have a JSONArray and other primitives.


Sign in to add a comment
Hosted by Google Code