Related
I've a problem trying to make my page printing out the JSONObject in the order i want. In my code, I entered this:
JSONObject myObject = new JSONObject();
myObject.put("userid", "User 1");
myObject.put("amount", "24.23");
myObject.put("success", "NO");
However, when I see the display on my page, it gives:
JSON formatted string: [{"success":"NO", "userid":"User 1", "bid":24.23}]
I need it in the order of userid, amount, then success. Already tried re-ordering in the code, but to no avail. I've also tried .append....need some help here thanks!!
You cannot and should not rely on the ordering of elements within a JSON object.
From the JSON specification at https://www.json.org/
An object is an unordered set of
name/value pairs
As a consequence,
JSON libraries are free to rearrange the order of the elements as they see fit.
This is not a bug.
I agree with the other answers. You cannot rely on the ordering of JSON elements.
However if we need to have an ordered JSON, one solution might be to prepare a LinkedHashMap object with elements and convert it to JSONObject.
#Test
def void testOrdered() {
Map obj = new LinkedHashMap()
obj.put("a", "foo1")
obj.put("b", new Integer(100))
obj.put("c", new Double(1000.21))
obj.put("d", new Boolean(true))
obj.put("e", "foo2")
obj.put("f", "foo3")
obj.put("g", "foo4")
obj.put("h", "foo5")
obj.put("x", null)
JSONObject json = (JSONObject) obj
logger.info("Ordered Json : %s", json.toString())
String expectedJsonString = """{"a":"foo1","b":100,"c":1000.21,"d":true,"e":"foo2","f":"foo3","g":"foo4","h":"foo5"}"""
assertEquals(expectedJsonString, json.toString())
JSONAssert.assertEquals(JSONSerializer.toJSON(expectedJsonString), json)
}
Normally the order is not preserved as below.
#Test
def void testUnordered() {
Map obj = new HashMap()
obj.put("a", "foo1")
obj.put("b", new Integer(100))
obj.put("c", new Double(1000.21))
obj.put("d", new Boolean(true))
obj.put("e", "foo2")
obj.put("f", "foo3")
obj.put("g", "foo4")
obj.put("h", "foo5")
obj.put("x", null)
JSONObject json = (JSONObject) obj
logger.info("Unordered Json : %s", json.toString(3, 3))
String unexpectedJsonString = """{"a":"foo1","b":100,"c":1000.21,"d":true,"e":"foo2","f":"foo3","g":"foo4","h":"foo5"}"""
// string representation of json objects are different
assertFalse(unexpectedJsonString.equals(json.toString()))
// json objects are equal
JSONAssert.assertEquals(JSONSerializer.toJSON(unexpectedJsonString), json)
}
You may check my post too: http://www.flyingtomoon.com/2011/04/preserving-order-in-json.html
u can retain the order, if u use JsonObject that belongs to com.google.gson :D
JsonObject responseObj = new JsonObject();
responseObj.addProperty("userid", "User 1");
responseObj.addProperty("amount", "24.23");
responseObj.addProperty("success", "NO");
Usage of this JsonObject doesn't even bother using Map<>
CHEERS!!!
Real answer can be found in specification, json is unordered.
However as a human reader I ordered my elements in order of importance. Not only is it a more logic way, it happened to be easier to read. Maybe the author of the specification never had to read JSON, I do.. So, Here comes a fix:
/**
* I got really tired of JSON rearranging added properties.
* Specification states:
* "An object is an unordered set of name/value pairs"
* StackOverflow states:
* As a consequence, JSON libraries are free to rearrange the order of the elements as they see fit.
* I state:
* My implementation will freely arrange added properties, IN SEQUENCE ORDER!
* Why did I do it? Cause of readability of created JSON document!
*/
private static class OrderedJSONObjectFactory {
private static Logger log = Logger.getLogger(OrderedJSONObjectFactory.class.getName());
private static boolean setupDone = false;
private static Field JSONObjectMapField = null;
private static void setupFieldAccessor() {
if( !setupDone ) {
setupDone = true;
try {
JSONObjectMapField = JSONObject.class.getDeclaredField("map");
JSONObjectMapField.setAccessible(true);
} catch (NoSuchFieldException ignored) {
log.warning("JSONObject implementation has changed, returning unmodified instance");
}
}
}
private static JSONObject create() {
setupFieldAccessor();
JSONObject result = new JSONObject();
try {
if (JSONObjectMapField != null) {
JSONObjectMapField.set(result, new LinkedHashMap<>());
}
}catch (IllegalAccessException ignored) {}
return result;
}
}
from lemiorhan example
i can solve with just change some line of lemiorhan's code
use:
JSONObject json = new JSONObject(obj);
instead of this:
JSONObject json = (JSONObject) obj
so in my test code is :
Map item_sub2 = new LinkedHashMap();
item_sub2.put("name", "flare");
item_sub2.put("val1", "val1");
item_sub2.put("val2", "val2");
item_sub2.put("size",102);
JSONArray itemarray2 = new JSONArray();
itemarray2.add(item_sub2);
itemarray2.add(item_sub2);//just for test
itemarray2.add(item_sub2);//just for test
Map item_sub1 = new LinkedHashMap();
item_sub1.put("name", "flare");
item_sub1.put("val1", "val1");
item_sub1.put("val2", "val2");
item_sub1.put("children",itemarray2);
JSONArray itemarray = new JSONArray();
itemarray.add(item_sub1);
itemarray.add(item_sub1);//just for test
itemarray.add(item_sub1);//just for test
Map item_root = new LinkedHashMap();
item_root.put("name", "flare");
item_root.put("children",itemarray);
JSONObject json = new JSONObject(item_root);
System.out.println(json.toJSONString());
JavaScript objects, and JSON, have no way to set the order for the keys. You might get it right in Java (I don't know how Java objects work, really) but if it's going to a web client or another consumer of the JSON, there is no guarantee as to the order of keys.
Download "json simple 1.1 jar" from this https://code.google.com/p/json-simple/downloads/detail?name=json_simple-1.1.jar&can=2&q=
And add the jar file to your lib folder
using JSONValue you can convert LinkedHashMap to json string
For those who're using maven, please try com.github.tsohr/json
<!-- https://mvnrepository.com/artifact/com.github.tsohr/json -->
<dependency>
<groupId>com.github.tsohr</groupId>
<artifactId>json</artifactId>
<version>0.0.1</version>
</dependency>
It's forked from JSON-java but switch its map implementation with LinkedHashMap which #lemiorhan noted above.
As all are telling you, JSON does not maintain "sequence" but array does, maybe this could convince you:
Ordered JSONObject
For Java code, Create a POJO class for your object instead of a JSONObject.
and use JSONEncapsulator for your POJO class.
that way order of elements depends on the order of getter setters in your POJO class.
for eg. POJO class will be like
Class myObj{
String userID;
String amount;
String success;
// getter setters in any order that you want
and where you need to send your json object in response
JSONContentEncapsulator<myObj> JSONObject = new JSONEncapsulator<myObj>("myObject");
JSONObject.setObject(myObj);
return Response.status(Status.OK).entity(JSONObject).build();
The response of this line will be
{myObject : {//attributes order same as getter setter order.}}
The main intention here is to send an ordered JSON object as response. We don't need javax.json.JsonObject to achieve that. We could create the ordered json as a string.
First create a LinkedHashMap with all key value pairs in required order. Then generate the json in string as shown below.
Its much easier with Java 8.
public Response getJSONResponse() {
Map<String, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("A", "1");
linkedHashMap.put("B", "2");
linkedHashMap.put("C", "3");
String jsonStr = linkedHashMap.entrySet().stream()
.map(x -> "\"" + x.getKey() + "\":\"" + x.getValue() + "\"")
.collect(Collectors.joining(",", "{", "}"));
return Response.ok(jsonStr).build();
}
The response return by this function would be following:
{"A":"1","B":"2","C":"3"}
Underscore-java uses linkedhashmap to store key/value for json. I am the maintainer of the project.
Map<String, Object> myObject = new LinkedHashMap<>();
myObject.put("userid", "User 1");
myObject.put("amount", "24.23");
myObject.put("success", "NO");
System.out.println(U.toJson(myObject));
I found a "neat" reflection tweak on "the interwebs" that I like to share.
(origin: https://towardsdatascience.com/create-an-ordered-jsonobject-in-java-fb9629247d76)
It is about to change underlying collection in org.json.JSONObject to an un-ordering one (LinkedHashMap) by reflection API.
I tested succesfully:
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import org.json.JSONObject;
private static void makeJSONObjLinear(JSONObject jsonObject) {
try {
Field changeMap = jsonObject.getClass().getDeclaredField("map");
changeMap.setAccessible(true);
changeMap.set(jsonObject, new LinkedHashMap<>());
changeMap.setAccessible(false);
} catch (IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
}
[...]
JSONObject requestBody = new JSONObject();
makeJSONObjLinear(requestBody);
requestBody.put("username", login);
requestBody.put("password", password);
[...]
// returned '{"username": "billy_778", "password": "********"}' == unordered
// instead of '{"password": "********", "username": "billy_778"}' == ordered (by key)
Just add the order with this tag
#JsonPropertyOrder({ "property1", "property2"})
Not sure if I am late to the party but I found this nice example that overrides the JSONObject constructor and makes sure that the JSON data are output in the same way as they are added. Behind the scenes JSONObject uses the MAP and MAP does not guarantee the order hence we need to override it to make sure we are receiving our JSON as per our order.
If you add this to your JSONObject then the resulting JSON would be in the same order as you have created it.
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import org.json.JSONObject;
import lombok.extern.java.Log;
#Log
public class JSONOrder {
public static void main(String[] args) throws IOException {
JSONObject jsontest = new JSONObject();
try {
Field changeMap = jsonEvent.getClass().getDeclaredField("map");
changeMap.setAccessible(true);
changeMap.set(jsonEvent, new LinkedHashMap<>());
changeMap.setAccessible(false);
} catch (IllegalAccessException | NoSuchFieldException e) {
log.info(e.getMessage());
}
jsontest.put("one", "I should be first");
jsonEvent.put("two", "I should be second");
jsonEvent.put("third", "I should be third");
System.out.println(jsonEvent);
}
}
Just use LinkedHashMap to keep de order and transform it to json with jackson
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.LinkedHashMap;
LinkedHashMap<String, Object> obj = new LinkedHashMap<String, Object>();
stats.put("aaa", "aaa");
stats.put("bbb", "bbb");
stats.put("ccc", "ccc");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
System.out.println(json);
maven dependency
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.10.7</version>
</dependency>
I just want the order for android unit tests that are somehow randomly changing overtime with this cool org.json.JSONObject, even thou it looks like it uses linked map but probably depends on api you compile it with or something, so it has different impl. with different android api probably.
I would suggest something like this:
object Json {
#SuppressLint("DiscouragedPrivateApi")
fun Object() = org.json.JSONObject().apply {
runCatching {
val nameValuePairs: Field = javaClass.getDeclaredField("nameValuePairs")
nameValuePairs.isAccessible = true
nameValuePairs.set(this, LinkedHashMap<String, Any?>())
}.onFailure { it.printStackTrace() }
}
}
Usage:
val jsonObject = Json.Object()
...
This is just some possibility I use it little differently so I modified it to post here. Sure gson or other lib is another option.
Suggestions that specification is bla bla are so shortsighted here, why you guys even post it, who cares about 15 years old json spec, everyone wants it ordered anyway.
I've a problem trying to make my page printing out the JSONObject in the order i want. In my code, I entered this:
JSONObject myObject = new JSONObject();
myObject.put("userid", "User 1");
myObject.put("amount", "24.23");
myObject.put("success", "NO");
However, when I see the display on my page, it gives:
JSON formatted string: [{"success":"NO", "userid":"User 1", "bid":24.23}]
I need it in the order of userid, amount, then success. Already tried re-ordering in the code, but to no avail. I've also tried .append....need some help here thanks!!
You cannot and should not rely on the ordering of elements within a JSON object.
From the JSON specification at https://www.json.org/
An object is an unordered set of
name/value pairs
As a consequence,
JSON libraries are free to rearrange the order of the elements as they see fit.
This is not a bug.
I agree with the other answers. You cannot rely on the ordering of JSON elements.
However if we need to have an ordered JSON, one solution might be to prepare a LinkedHashMap object with elements and convert it to JSONObject.
#Test
def void testOrdered() {
Map obj = new LinkedHashMap()
obj.put("a", "foo1")
obj.put("b", new Integer(100))
obj.put("c", new Double(1000.21))
obj.put("d", new Boolean(true))
obj.put("e", "foo2")
obj.put("f", "foo3")
obj.put("g", "foo4")
obj.put("h", "foo5")
obj.put("x", null)
JSONObject json = (JSONObject) obj
logger.info("Ordered Json : %s", json.toString())
String expectedJsonString = """{"a":"foo1","b":100,"c":1000.21,"d":true,"e":"foo2","f":"foo3","g":"foo4","h":"foo5"}"""
assertEquals(expectedJsonString, json.toString())
JSONAssert.assertEquals(JSONSerializer.toJSON(expectedJsonString), json)
}
Normally the order is not preserved as below.
#Test
def void testUnordered() {
Map obj = new HashMap()
obj.put("a", "foo1")
obj.put("b", new Integer(100))
obj.put("c", new Double(1000.21))
obj.put("d", new Boolean(true))
obj.put("e", "foo2")
obj.put("f", "foo3")
obj.put("g", "foo4")
obj.put("h", "foo5")
obj.put("x", null)
JSONObject json = (JSONObject) obj
logger.info("Unordered Json : %s", json.toString(3, 3))
String unexpectedJsonString = """{"a":"foo1","b":100,"c":1000.21,"d":true,"e":"foo2","f":"foo3","g":"foo4","h":"foo5"}"""
// string representation of json objects are different
assertFalse(unexpectedJsonString.equals(json.toString()))
// json objects are equal
JSONAssert.assertEquals(JSONSerializer.toJSON(unexpectedJsonString), json)
}
You may check my post too: http://www.flyingtomoon.com/2011/04/preserving-order-in-json.html
u can retain the order, if u use JsonObject that belongs to com.google.gson :D
JsonObject responseObj = new JsonObject();
responseObj.addProperty("userid", "User 1");
responseObj.addProperty("amount", "24.23");
responseObj.addProperty("success", "NO");
Usage of this JsonObject doesn't even bother using Map<>
CHEERS!!!
Real answer can be found in specification, json is unordered.
However as a human reader I ordered my elements in order of importance. Not only is it a more logic way, it happened to be easier to read. Maybe the author of the specification never had to read JSON, I do.. So, Here comes a fix:
/**
* I got really tired of JSON rearranging added properties.
* Specification states:
* "An object is an unordered set of name/value pairs"
* StackOverflow states:
* As a consequence, JSON libraries are free to rearrange the order of the elements as they see fit.
* I state:
* My implementation will freely arrange added properties, IN SEQUENCE ORDER!
* Why did I do it? Cause of readability of created JSON document!
*/
private static class OrderedJSONObjectFactory {
private static Logger log = Logger.getLogger(OrderedJSONObjectFactory.class.getName());
private static boolean setupDone = false;
private static Field JSONObjectMapField = null;
private static void setupFieldAccessor() {
if( !setupDone ) {
setupDone = true;
try {
JSONObjectMapField = JSONObject.class.getDeclaredField("map");
JSONObjectMapField.setAccessible(true);
} catch (NoSuchFieldException ignored) {
log.warning("JSONObject implementation has changed, returning unmodified instance");
}
}
}
private static JSONObject create() {
setupFieldAccessor();
JSONObject result = new JSONObject();
try {
if (JSONObjectMapField != null) {
JSONObjectMapField.set(result, new LinkedHashMap<>());
}
}catch (IllegalAccessException ignored) {}
return result;
}
}
from lemiorhan example
i can solve with just change some line of lemiorhan's code
use:
JSONObject json = new JSONObject(obj);
instead of this:
JSONObject json = (JSONObject) obj
so in my test code is :
Map item_sub2 = new LinkedHashMap();
item_sub2.put("name", "flare");
item_sub2.put("val1", "val1");
item_sub2.put("val2", "val2");
item_sub2.put("size",102);
JSONArray itemarray2 = new JSONArray();
itemarray2.add(item_sub2);
itemarray2.add(item_sub2);//just for test
itemarray2.add(item_sub2);//just for test
Map item_sub1 = new LinkedHashMap();
item_sub1.put("name", "flare");
item_sub1.put("val1", "val1");
item_sub1.put("val2", "val2");
item_sub1.put("children",itemarray2);
JSONArray itemarray = new JSONArray();
itemarray.add(item_sub1);
itemarray.add(item_sub1);//just for test
itemarray.add(item_sub1);//just for test
Map item_root = new LinkedHashMap();
item_root.put("name", "flare");
item_root.put("children",itemarray);
JSONObject json = new JSONObject(item_root);
System.out.println(json.toJSONString());
JavaScript objects, and JSON, have no way to set the order for the keys. You might get it right in Java (I don't know how Java objects work, really) but if it's going to a web client or another consumer of the JSON, there is no guarantee as to the order of keys.
Download "json simple 1.1 jar" from this https://code.google.com/p/json-simple/downloads/detail?name=json_simple-1.1.jar&can=2&q=
And add the jar file to your lib folder
using JSONValue you can convert LinkedHashMap to json string
For those who're using maven, please try com.github.tsohr/json
<!-- https://mvnrepository.com/artifact/com.github.tsohr/json -->
<dependency>
<groupId>com.github.tsohr</groupId>
<artifactId>json</artifactId>
<version>0.0.1</version>
</dependency>
It's forked from JSON-java but switch its map implementation with LinkedHashMap which #lemiorhan noted above.
As all are telling you, JSON does not maintain "sequence" but array does, maybe this could convince you:
Ordered JSONObject
For Java code, Create a POJO class for your object instead of a JSONObject.
and use JSONEncapsulator for your POJO class.
that way order of elements depends on the order of getter setters in your POJO class.
for eg. POJO class will be like
Class myObj{
String userID;
String amount;
String success;
// getter setters in any order that you want
and where you need to send your json object in response
JSONContentEncapsulator<myObj> JSONObject = new JSONEncapsulator<myObj>("myObject");
JSONObject.setObject(myObj);
return Response.status(Status.OK).entity(JSONObject).build();
The response of this line will be
{myObject : {//attributes order same as getter setter order.}}
The main intention here is to send an ordered JSON object as response. We don't need javax.json.JsonObject to achieve that. We could create the ordered json as a string.
First create a LinkedHashMap with all key value pairs in required order. Then generate the json in string as shown below.
Its much easier with Java 8.
public Response getJSONResponse() {
Map<String, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("A", "1");
linkedHashMap.put("B", "2");
linkedHashMap.put("C", "3");
String jsonStr = linkedHashMap.entrySet().stream()
.map(x -> "\"" + x.getKey() + "\":\"" + x.getValue() + "\"")
.collect(Collectors.joining(",", "{", "}"));
return Response.ok(jsonStr).build();
}
The response return by this function would be following:
{"A":"1","B":"2","C":"3"}
Underscore-java uses linkedhashmap to store key/value for json. I am the maintainer of the project.
Map<String, Object> myObject = new LinkedHashMap<>();
myObject.put("userid", "User 1");
myObject.put("amount", "24.23");
myObject.put("success", "NO");
System.out.println(U.toJson(myObject));
I found a "neat" reflection tweak on "the interwebs" that I like to share.
(origin: https://towardsdatascience.com/create-an-ordered-jsonobject-in-java-fb9629247d76)
It is about to change underlying collection in org.json.JSONObject to an un-ordering one (LinkedHashMap) by reflection API.
I tested succesfully:
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import org.json.JSONObject;
private static void makeJSONObjLinear(JSONObject jsonObject) {
try {
Field changeMap = jsonObject.getClass().getDeclaredField("map");
changeMap.setAccessible(true);
changeMap.set(jsonObject, new LinkedHashMap<>());
changeMap.setAccessible(false);
} catch (IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
}
[...]
JSONObject requestBody = new JSONObject();
makeJSONObjLinear(requestBody);
requestBody.put("username", login);
requestBody.put("password", password);
[...]
// returned '{"username": "billy_778", "password": "********"}' == unordered
// instead of '{"password": "********", "username": "billy_778"}' == ordered (by key)
Just add the order with this tag
#JsonPropertyOrder({ "property1", "property2"})
Not sure if I am late to the party but I found this nice example that overrides the JSONObject constructor and makes sure that the JSON data are output in the same way as they are added. Behind the scenes JSONObject uses the MAP and MAP does not guarantee the order hence we need to override it to make sure we are receiving our JSON as per our order.
If you add this to your JSONObject then the resulting JSON would be in the same order as you have created it.
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import org.json.JSONObject;
import lombok.extern.java.Log;
#Log
public class JSONOrder {
public static void main(String[] args) throws IOException {
JSONObject jsontest = new JSONObject();
try {
Field changeMap = jsonEvent.getClass().getDeclaredField("map");
changeMap.setAccessible(true);
changeMap.set(jsonEvent, new LinkedHashMap<>());
changeMap.setAccessible(false);
} catch (IllegalAccessException | NoSuchFieldException e) {
log.info(e.getMessage());
}
jsontest.put("one", "I should be first");
jsonEvent.put("two", "I should be second");
jsonEvent.put("third", "I should be third");
System.out.println(jsonEvent);
}
}
Just use LinkedHashMap to keep de order and transform it to json with jackson
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.LinkedHashMap;
LinkedHashMap<String, Object> obj = new LinkedHashMap<String, Object>();
stats.put("aaa", "aaa");
stats.put("bbb", "bbb");
stats.put("ccc", "ccc");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
System.out.println(json);
maven dependency
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.10.7</version>
</dependency>
I just want the order for android unit tests that are somehow randomly changing overtime with this cool org.json.JSONObject, even thou it looks like it uses linked map but probably depends on api you compile it with or something, so it has different impl. with different android api probably.
I would suggest something like this:
object Json {
#SuppressLint("DiscouragedPrivateApi")
fun Object() = org.json.JSONObject().apply {
runCatching {
val nameValuePairs: Field = javaClass.getDeclaredField("nameValuePairs")
nameValuePairs.isAccessible = true
nameValuePairs.set(this, LinkedHashMap<String, Any?>())
}.onFailure { it.printStackTrace() }
}
}
Usage:
val jsonObject = Json.Object()
...
This is just some possibility I use it little differently so I modified it to post here. Sure gson or other lib is another option.
Suggestions that specification is bla bla are so shortsighted here, why you guys even post it, who cares about 15 years old json spec, everyone wants it ordered anyway.
I'm new to using protobuf, and was wondering if there is a simple way to convert a json stream/string to a protobuf stream/string in Java?
For example,
protoString = convertToProto(jsonString)
I have a json string that I want to parse into a protobuf message. So, I want to first convert the json string to protobuf, and then call Message.parseFrom() on it.
With proto3 you can do this using JsonFormat. It parses directly from the JSON representation, so there is no need for separately calling MyMessage.parseFrom(...). Something like this should work:
JsonFormat.parser().merge(json_string, builder);
//You can use this for converting your input json to a Struct / any other Protobuf Class
import com.google.protobuf.Struct.Builder;
import com.google.protobuf.Struct;
import com.google.protobuf.util.JsonFormat;
import org.json.JSONObject;
JSONObject parameters = new JSONObject();
Builder structBuilder = Struct.newBuilder();
JsonFormat.parser().merge(parameters.toString(), structBuilder);
// Now use the structBuilder to pass below (I used it for Dialog Flow V2 Context Management)
Since someone asked about getting the exception "com.google.protobuf.InvalidProtocolBufferException: JsonObject" when following Adam's advice--I ran into the same issue. Turns out it was due to the google protobuf timestamps. They are being serialized as an object containing two fields "seconds" and "nanos", since this isn't production code, I just got around this by parsing the JSON using jackson, going through the JSON object recursively and changing every timestamp from an object to a string formatted as per RFC 3339, I then serialized it back out and used the protobuf JSON parser as Adam has shown. This fixed the issue. This is some throwaway code I wrote (in my case all timestamp fields contain the word "timestamp", this could be more robust, but I don't care):
public Map<String, Object> fixJsonTimestamps(Map<String, Object> inMap) {
Map<String, Object> outMap = new HashMap<>();
for(String key : inMap.keySet()) {
Object val = inMap.get(key);
if(val instanceof Map) {
Map<String, Object> valMap = (Map<String, Object>)val;
if(key.toLowerCase().contains("timestamp") &&
valMap.containsKey("seconds") && valMap.containsKey("nanos")) {
if(valMap.get("seconds") != null) {
ZonedDateTime d = ZonedDateTime.ofInstant(Instant.ofEpochSecond((int)valMap.get("seconds")).plusNanos((int)valMap.get("nanos")),
ZoneId.of("UTC"));
val = d.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"));
}
} else {
val = fixJsonTimestamps(valMap);
}
} else if(val instanceof List && ((List) val).size() > 0 &&
((List) val).get(0) instanceof Map) {
List<Map<String, Object>> outputList = new ArrayList<>();
for(Map item : (List<Map>)val) {
outputList.add(fixJsonTimestamps(item));
}
val = outputList;
}
outMap.put(key, val);
}
return outMap;
}
Not the most ideal solution but it works for what I am doing, I think I saw someone recommend using a different timestamp class.
You can convert json string to Proto using builder and json String
Example :
YourProto.Builder protoBuilder = YourProto.newBuilder();
JsonFormat.parser().merge(JsonString, protoBuilder);
If you want to ignore unknown json field then
YourProto.Builder protoBuilder = YourProto.newBuilder();
JsonFormat.parser()..ignoringUnknownFields().merge(JsonString, protoBuilder);
Another way is, to use mergeFrom method from ProtocolBuffer
Example :
YourProto.Builder protoBuilder = YourProto.newBuilder();
protoBuilder.mergeFrom(JsonString.getBytes());
Once it execute, you will get all the data in protoBuilder from json String
online service:
https://json-to-proto.github.io/
This tool instantly converts JSON into a Protobuf. Paste a JSON structure on the left and the equivalent Protobuf will be generated to the right, which you can paste into your program. The script has to make some assumptions, so double-check the output!
Actually i want to make a MCQ for a medical application in Android ! So i want to get question and my possible choice from my database but i have a problem when i try to get my question witch him choice. My error is showed by the JSONException and i don't know why :(
I check my JSON with jsonlint.com so i think it's ok for that.
Here is my JSON :
{
"QCM": {
"1": {
"question": "Est-ce que Captain America gagne contre IronMan",
"id": "31"
},
"2": {
"choix": "Oui"
},
"3": {
"choix": "Non"
}
}
}
and here is my JAVA from my Android application.
try {
JSONObject lesQuestions = response.getJSONObject("QCM");
Iterator<?> keys = lesQuestions.keys();
while(keys.hasNext()) {
String key = (String) keys.next();
if (lesQuestions.get(key) instanceof JSONObject) {
JSONObject obj = (JSONObject) lesQuestions.get(key);
String signesCliniques = obj.getString("question");
String choix = obj.getString("choix");
lesChoixButton.setText(choix);
symptomesQuestions.setText(signesCliniques);
}
}
}
i hope you can help me !
Not every JSONObject in your JSON data has "question" and "choix" key. I am quite sure that you are getting org.json.JSONException: No value for ......
Make sure to check if there is "question" or "choix" parameter before you attempt to access it.
EDIT:
To check, you can use JSONObject.has(String parameter) or JSONObject.isNull(String parameter) method. Link: http://developer.android.com/reference/org/json/JSONObject.html#has(java.lang.String)
I suggest changing the structure of your JSON. Having a JSONObject for the question and a JSONArray for the Choices. Still up to you though if you still want to proceed with your current implementation. What you could try to use is JSON.optString(). It would return a value depending if the key exists, else it will return an empty string. As per its description:
Returns the value mapped by name if it exists, coercing it if necessary, or the empty string if no such mapping exists.
Use it like so, change your:
String choix = obj.getString("choix");
to
String choix = obj.optString("choix");
It should work. You can make it better though.
The Correct answer is, first when you get your response from your server, parse/convert it into JSONObject like
JSONObject rootObject = new JSONObject (response.toString());
and Then follow your JSON parsing as you are doing. This will do for sure.
JSONObject lesQuestions = rootObject.getJSONObject("QCM");
Android JSON parsers provide another optional method which begins with "opt" in comparison to methods which begin with "get". The significance with opt is that, it will return a NULL result and not throw an exception.
E.g:
obj.getString("question"); will throw an exception if the JSON does not have a key with value "question."
However,
String s = obj.optString("question"); will NOT throw an exception if the JSON does not have a key with value "question". Your result string will simply be NULL.
I find the opt methods very handy rather than the get methods which throws exceptions.
I have below JSON response in which I need to check whether response field has null value or not. If response field has null value then I need to exit out of the program.
[
{
"results": {
"response": null,
"type": "ABC"
},
"error": null
}
]
What is the easiest way to check this out? One option I know is to convert JSON to POJO and then check response field. Is there any other way?
If you are using codehouse's JSON library , you could do something like this:
JSONObject jsonObj = new JSONObject(jsonString);
System.out.println(jsonObj .isNull("error") ? " error is null ":" error is not null" );
if using Google's gson :
JsonObject jsonObject = new JsonParser().parse(st).getAsJsonObject();
JsonElement el = jsonObject.get("error");
if (el != null && !el.isJsonNull()){
System.out.println (" not null");
}else{
System.out.println (" is null");
}
I am using org.json.JSONObject. This is an example that you can use to test a JSONObject is null or not.
package general;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
public class CheckNullInJSONObject {
public static void main(String[] args) {
JSONObject json = new JSONObject("{results : [{response:null}, {type:ABC}], error:null}");
JSONArray array = json.getJSONArray("results");
try {
for(int i = 0 ; i < array.length() ; i++){
JSONObject response = array.getJSONObject(i);
if (response.isNull("response")){
throw new Exception("Null value found");
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
Regex/parse string to get the response field value or use google gson lib: https://github.com/google/gson to create object and access any field.
The safest way to reliably check the value of the response field for null is (as you suggested) to model the json data structure with POJO classes and use a json library such as Gson or Jackson to deserialize your json to your POJOs.
Don't listen to other answers here advising the use of regex. Building a correct and reliable json parser using only regex is a) bug prone and b) can have poor performance.
Two ways depending on your needs:
Quick and dirty way which might actually be useful/good enough/good performance:
String jsonString = ...
jsonString.contains("\"response\": null");
Yes, it's error prone if the server changes anything, even line breaks etc. But it will be using a lot less resources.
Variations with higher tolerance include regexp that simply allow zero or multiple whitespace between field name and value. Another variant is to find the index of the field and look for the value after that manually:
int fieldIndex = jsonString.indexOf("\"response\":");
//left as an exercise...
Json parsing with a library, such as Gson (Google's json library):
Simple minimum Results class:
public static class Result {
public static class Results {
public String response;
}
public Results results;
}
Parse and check (ignoring null and length check of array):
Gson gson = new Gson();
Result[] results = gson.fromJson(jsonString, Result[].class);
System.out.println(results[0].results.response);
Gson can be found here:
https://github.com/google/gson