|
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 factoryYou 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 handlerJSON.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 handlerPlease 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 extensionsJSONParser 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:
In both cases, a liberal parser will do nothing harmful to your application. The reason of accepting something like [5,,2] is that:
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. |
Sign in to add a comment
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.
RFC 4627 states:
It does not cause interoperability issues because the encoding is always right, and JSON.simple always accepts inputs that conform to the JSON grammar.
Actually, JSON.org's RI goes further on extension. It accepts single quote strings and hex number: : 0x20?.
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. :-)
Please read RFC4627 carefully, then you'll find the lines as below:
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.
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.
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.
"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.
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:
In both cases, a liberal parser will do nothing harmful to your application.
The reason of accepting something like [5,,2] is that:
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.
Any particular reason why JSONValue.parse(s) returns an Object and not a more proper JSONObject?
Because we also have a JSONArray and other primitives.