Have an array, when the size is 1, the json data I received does NOT contains []; like
{"firstname":"tom"}
when the size is larger than 1, the data I received contains [], like
[{"firstname":"tom"},{"firstname":"robert"}]
Currently my class contains an array property
String[] firstname;
//getter setter omit here
Code to handle this likes
ObjectMapper mapper = new ObjectMapper();
MyClass object = mapper.readValue(json, MyClass.class);
When the size is larger than 1, the deserialization works. However when size is 1, the deserialization failed.
I am currently using jackson, any solution for this problem?
I am wondering if jackson/gson or any other library can handle this?
For Jackson specifically, your best bet would to first bind to a JsonNode or Object, like:
Object raw = objectMapper.readValue(json, Object.class); // becomes Map, List, String etc
and then check what you got, bind again:
MyClass[] result;
if (raw instanceof List<?>) { // array
result = objectMapper.convertValue(raw, MyClass[].class);
} else { // single object
result = objectMapper.convertValue(raw, MyClass.class);
}
But I think JSON you are getting is bad -- why would you return an object, or array, intead of just array of size 1? -- so if at all possible, I'd rather fix JSON first. But if that is not possible, this would work.
Here's how to do it with GSON. Let's assume this object structure:
public class Group{
public Group(final List<Person> members){
this.members = members;
}
private final List<Person> members;
}
public class Person{
public Person(final String firstName, final String lastName){
this.firstName = firstName;
this.lastName = lastName;
}
private final String firstName;
private final String lastName;
}
Here's a deserializer that understands single Person entries as well as arrays of them:
public class GroupDeserializer implements JsonDeserializer<Group>{
#Override
public Group deserialize(final JsonElement json,
final Type typeOfT,
final JsonDeserializationContext context) throws JsonParseException{
List<Person> members;
if(json.isJsonArray()){
final JsonArray array = json.getAsJsonArray();
members = new ArrayList<Person>(array.size());
for(final JsonElement personElement : array){
members.add(getSinglePerson(personElement, context));
}
} else{
members =
Collections.singletonList(getSinglePerson(json, context));
}
return new Group(members);
}
private Person getSinglePerson(final JsonElement element,
final JsonDeserializationContext context){
final JsonObject personObject = element.getAsJsonObject();
final String firstName =
personObject.getAsJsonPrimitive("firstname").getAsString();
final String lastName =
personObject.getAsJsonPrimitive("lastname").getAsString();
return new Person(firstName, lastName);
}
}
And here you can find the necessary Configuration to use this
edit: I guess you would then just extract a JsonElement and check it's isJsonArray() and/or isJsonObject(). Then, just call getAsJsonArray() or getAsJsonObject().
Old answer: Why not just try to extract the array and catch the JsonParseException if it fails. In the catch block, try to extract an object instead.
I know it's not pretty but it should work.
I have faced the same issue when I am trying to deserialize the JSON object which was constructed from XML. (XML-JSON). After quite a bit of research, found that we have a simple fix.
Just set the feature : ACCEPT_SINGLE_VALUE_AS_ARRAY.
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
For more info : http://fasterxml.github.com/jackson-databind/javadoc/2.0.0/com/fasterxml/jackson/databind/DeserializationFeature.html#ACCEPT_SINGLE_VALUE_AS_ARRAY
In the first instance it looks like an object, in the second instance it looks like an array of objects (which it sounds like you are expecting).
JSON encoding libraries typically have a "force array" option for cases like this. Failing that, on your client you could check the JSON response and if it's not an array, push the returned objected into an new array.
Related
I'm testing Jersey, I wanted to mack a mock endpoint that produces this JSON object
{
"Flight1" : 757,
"Flight2" : 107,
"Flight3" : 637,
}
so I've written written this resource:
#GET
#Path("myjson")
#Produces(MediaType.APPLICATION_JSON)
public String getMyJson(#QueryParam ("test1") String lat, #QueryParam("test2") String lng) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("Flight 1", 765);
map.put("Flight 2", 657);
map.put("Flight 3", 908);
return map.toString();
}
But then I get this response when I call /myjson
{
Flight 1=765,
Flight 2=657,
Flight 3=908
}
Jersey already knows which element is a String and which element is an Integer, yet it format then as if they're all numbers.
Also the Json as it stands can not be formatted by the "pretty" formatter, and I believe this makes parsing it difficult by http clients.
So my question are:
Why is this happening?
How to avoid it and write simple mock JSON object for testing that is correctly formatted
You can add the Jaxb annotations to serialize and deserialize the response object directly without converting. For this you need to add the jersey's jaxb library so that when you jersey environment is getting booted, it can enable the auto conversion feature.
Example:
#Path("jaxbResource")
#Produces("application/xml")
#Consumes("application/xml")
public class UserResource {
#GET
public User[] getUserArray() {
List<User> userList = new ArrayList<User>();
userList.add(new User(1, "John"));
………
return userList.toArray(new User[userList.size()]);
}
}
#XmlRootElement
public class User {
private int id;
private String name;
public User() {}
public User(int id,String name) {
this.id = id;
this.name = name;
}
………
}
Hope this helps!!!
Why is this happening?
Because you are only making a toString of your HashMap.
Ex.
HashMap<String,String> stringStringHashMap = new HashMap<String, String>();
stringStringHashMap.put("a","b");
stringStringHashMap.put("b","b");
stringStringHashMap.put("c","b");
Will print {b=b, c=b, a=b}
How to avoid it and write simple mock JSON object for testing that is
correctly formatted
You can do this by using a lot of libraries(Gson, Jackson, JsonSimple,etc).
As this already answered what you want to make HashMap to Json
This has nothing to do with Jersey/Tomcat. For core Java programming, that is how best toString() method could process the map to String.
In order to do so you can convert to JSONObject using
String jon = JSONObject.valueToString(map);
System.out.println(jon);
OR even using the gson like
Gson gson = new Gson();
String json = gson.toJson(map);
System.out.println(json);
{
"localeCode": "",
"map": {
"DynamicName1": [],
"DynamicName2": [
{
"date": "2016-05-15T00:00:00",
"seqId": 1,
"status": 10
},
{
"date": "2016-05-16T00:00:00",
"seqId": 83,
"status": 10
}
],
"DynamicName3": [],
"DynamicName4": []
},
"respCode": 100,
"respMsg": "success",
"status": 1
}
How to correctly map this kind of json. If you can see that, Dynamic is a dynamic name. So far I have done this :
public class MapModel {
public MapObject map;
public static class MapObject{
public java.util.Map<String, Student> queryStudent;
public static class Student{
public String date;
public String seqId;
public String status;
}
}
}
But when run the app. I'm getting NullPointerException. Can somebody help me?
You're getting the NullPointerException accessing queryStudent of your MapObject inside your MapModel since it's not correctly filled when you're trying to deserialize your Json.
So to solve your problem look at Gson documentation where you can see:
You can serialize the collection with Gson without doing anything
specific: toJson(collection) would write out the desired output.
However, deserialization with fromJson(json, Collection.class) will
not work since Gson has no way of knowing how to map the input to the
types. Gson requires that you provide a genericised version of
collection type in fromJson(). So, you have three options:
Use Gson's parser API (low-level streaming parser or the DOM parser
JsonParser) to parse the array elements and then use Gson.fromJson()
on each of the array elements.This is the preferred approach. Here is
an example that demonstrates how to do this.
Register a type adapter for Collection.class that looks at each of the
array members and maps them to appropriate objects. The disadvantage
of this approach is that it will screw up deserialization of other
collection types in Gson.
Register a type adapter for MyCollectionMemberType and use fromJson()
with Collection.
Since your MapObject containts a java.util.Map but your class itself it's not generic, I think that a good approach for your case is create a Deserializer.
Before this try to clean up your class definition, to provide constructors to make the deserializer easy to build. Your POJO classes could be:
Student class
public class Student{
public String date;
public String seqId;
public String status;
public Student(String date, String seqId, String status){
this.date = date;
this.seqId = seqId;
this.status = status;
}
}
MapObject class
Note: I change you Map definition, since in your Json seems that could be multiple students for each DynamicName (look at DynamicName2 from your question), so I use Map<String,List<Student>> instead of Map<String,Student>:
public class MapObject{
public Map<String,List<Student>> queryStudent;
public MapObject(Map<String,List<Student>> value){
this.queryStudent = value;
}
}
MapModel class
public class MapModel {
public MapObject map;
}
Now create a Deserializer for your MapObject:
public class MapObjectDeserializer implements JsonDeserializer<MapObject> {
public MapObject deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
Map<String,List<Student>> queryStudents = new HashMap<String,List<Student>>();
// for each DynamicElement...
for (Map.Entry<String,JsonElement> entry : json.getAsJsonObject().entrySet()) {
List<Student> students = new ArrayList<Student>();
// each dynamicElement has an Array so convert and add an student
// for each array entry
for(JsonElement elem : entry.getValue().getAsJsonArray()){
students.add(new Gson().fromJson(elem,Student.class));
}
// put the dinamic name and student on the map
queryStudents.put(entry.getKey(),students);
}
// finally create the mapObject
return new MapObject(queryStudents);
}
}
Finally register the Deserializer and parse your Json:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(MapObject.class, new MapObjectDeserializer());
Gson gson = builder.create();
MapModel object = gson.fromJson(YourJson,MapModel.class);
DISCLAIMER: For fast prototyping I test this using groovy, I try to keep the Java syntax but I can forget something, anyway I think that this can put you on the right direction.
Hope it helps,
Is there was a way to pass a String into some Jackson object and have it populate the JSON obj for me? Maybe I'm comparing apples to oranges but the json-rpc-1.0.jar library allows me to do this:
// string will be read in from file but putting the string below just to show what i'm trying to do.
JSONObject jsonObj;
String testStr = "{"blah":123, "aaa": "got here", "foo":"bar", "bar":123}";
jsonObj = new JSONObject(testStr);
jsonObj.put("blah",345);
If I execute
System.out.println(jsonObj);
I get:
{"blah":345, "aaa": "got here", "foo":"bar", "bar":123}
The problem with the json-rpc-1.0.jar file is it doesn't play nicely with long primitive types. For some reason, it converts long data to something like 1.32e9 if I tried to assign a timestamp (long data type) to a field.
I found Jackson (jackson-core-2.2.3.jar) is nicer to longs, preserving the 10-13 digits I need for my timestamp. However, I can't find anything that works like the above snippet of code in Jackson. The closest might be ObjectMapper.readValue but it's not exactly like above.
Please let me know if this is possible or if I'm just dreaming. Thanks in advance for your help. In the meantime, I will try to look at the API some more.
IMO this is not how Jackson is meant to be used. With Jackson, an object should be serialized with the fields of its class. You shouldn't be adding anything to that JSON afterwards. For the sake of the question, however, here's what you can do. Take for example
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
MyClass a = new MyClass();
ObjectNode node = mapper.<ObjectNode>valueToTree(a);
node.put("blah", "123");
System.out.println(node);
}
static class MyClass {
private String value = "some text";
private long timestamp = System.currentTimeMillis();
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
}
which prints
{"value":"some text","timestamp":1384233053765,"blah":"123"}
The valueToTree() method will convert your object into an ObjectNode which is kind of a tree that holds the various JSON elements. You can modify this ObjectNode by adding or removing elements. That is what we do with node.put("blah", "123");. It will add a Json object with name blah and value "123".
I have a model class method which returns a list of objects which contains all the registered user details. I want to fetch the list resturned by all() method and convert the data into JSON object and pass it to the view like a string. How can I do this conversion of this array list to JSON object?
I was unable to do this by below:
ObjectMapper mapper = new ObjectMapper();
JSONObject json = new JSONObject();
JsonNodeFactory jsonnode = JsonNodeFactory.instance;
ObjectNode result = new ObjectNode(jsonnode);
for (int i = 0; i < list.size(); i++) {
json.put(list.get(i).fname, list.get(i));
System.out.println(json.get("fname"));
}
#Entity
class Mydata extends Model {
#Id
public Long Id;
public String fname;
public String lname;
public String city;
public String state;
/****************** READ/select OPERATION *****************/
public static Finder < Long, Mydata > finder = new Finder(Long.class, Mydata.class);
public static List < Mydata > all() {
return finder.all();
}
public static void createuser(Mydata user) {
user.save();
}
}
To convert ArrayList to Json, just download Open Source json utility from:
http://json.org/java/ or Jar file from here
And just do:
JSONArray jsonAraay = new JSONArray(your_array_list);
That's it
Note: You should have setter/getter in your POJO/MODEL class to convert arraylist to json
Don't bother with org.json, use Jackson all the way:
// list is a List<MyData>
final ObjectMapper mapper = new ObjectMapper();
final Map<String, MyData> map = new HashMap<>();
for (final MyData data: list)
map.put(data.fname, data);
final JsonNode json = mapper.valueToTree(map);
You could use all sorts of third party libraries like others here have suggested, or just use Play's own simplified methods for this (found in play.libs.Json) which works with Jackson objects, but it is integrated into the framework and requires a lot less code to use, for example:
JsonNode myJsonNode = Json.toJson(MyListObject); which converts the List to a JsonNode object, then use something like String jsonResult = Json.stringify(myJsonNode); to convert it into a string representation.
If you are using the JSON in a template, don't forget to wrap it in something like #Html(myJsonString) so it will not escape anything. Otherwise, if you are just outputting the pure JSON to the browser, a simple return ok(jsonResult); will work as Play will automatically set the content type.
Reference link: http://www.playframework.com/documentation/api/2.0/java/play/libs/Json.html
have you looked at this:
http://www.json.org/javadoc/org/json/JSONObject.html#valueToString(java.lang.Object)
JSONObject.valueToString(<<your list of custom object>> OR <<objects>> OR <<map>>)
works just fine...there are some other methods on that lib, if you are interested....
Using GSON, how can i return a single key from a Multidimensional Json String?
Here is the Multidimensional Json String:
{"statusCode":0,"statusDescription":"OK","data":{"user":{"id":xxx,"company_id":xxx,"account_type":"5","enable_locations":true,"intuit_user_id":null,"nick_name":"xxx","is_owner":"1","enabled":"1"},"session_token":"xxx"}}
I want to return the "session_token" key value.
I'm trying this:
class app {
static class Response {
String session_token;
}
public void getSessionToken() {
String x = {"statusCode":0,"statusDescription":"OK","data":{"user":{"id":xxx,"company_id":xxx,"account_type":"5","enable_locations":true,"intuit_user_id":null,"nick_name":"xxx","is_owner":"1","enabled":"1"},"session_token":"xxx"}}
Response r = new Gson().fromJson(x, Response.class);
System.out.println(r.session_token);
}
}
But with this, my r.session_token returns null.
You would need to use Gson's JsonParser class directly and extract the data from the parse tree:
String myJsonString = "{\"name\":\"john\",\"lastname\":\"smith\"}";
JsonParser parser = new JsonParser();
JsonElement element = parser.parse(myJsonString);
JsonObject jsonObject = element.getAsJsonObject();
String lastName = jsonObject.get("lastname").getAsString();
System.out.println(lastName);
That said, it's debatable whether this would save you any real time over:
(edited from comments below):
class App {
static class Response {
String lastname;
}
public static void main(String[] args) {
String myJsonString = "{\"name\":\"john\",\"lastname\":\"smith\"}";
Response r = new Gson().fromJson(myJsonString, Response.class);
System.out.println(r.lastname);
}
}
Gson will silently ignore the fact that there's more data in the JSON than you're interested in, and later on you might be interested in it, in which case it's trivial to add fields to your Response class.
Edit due to question changing:
You have a JSON object. It contains a field data whose value is an object. Inside that object you have a field session_token that you're interested in.
Either you have to navigate to that field through the parse tree, or you have to create Java classes that all will map to. The Java classes would resemble (at the bare minimum):
class Response {
Data data;
}
class Data {
String session_token;
}