I'm trying to get Java Object from JSON string that has inner arrays, there are pretty same questions, but none couldn't solve my problem. Now in console i get MethodPackage.JsonDeserialize#6580cfdd (I'm doing with objectmapper)
My aim is to get separately values in json to do some manupulations
below is my full code:
JSONstring:
{
"status": 1,
"message": "ok",
"sheduleCod": "NOST_A_Persons_m_noaccum",
"algorithms": [{
"cod": "No_st_alg_1",
"kcp": "U6000427",
"dtBeg": "2017-11-01 00:00:00",
"dtEnd": "2017-12-01 00:00:00"
}, {
"cod": "No_st_alg_2",
"kcp": "U6000427",
"dtBeg": "2017-11-01 00:00:00",
"dtEnd": "2017-12-01 00:00:00"
}, {
"cod": "No_st_alg_3",
"kcp": "U6000427",
"dtBeg": "2017-11-01 00:00:00",
"dtEnd": "2017-12-01 00:00:00"
}]
}
Main.class
String jsonString = response.toString();
JsonDeserialize deserialize = objectMapper.readValue(jsonString, JsonDeserialize.class);
System.out.println(deserialize);}
JsonDeserialize.class
public class JsonDeserialize {
private String status;
private String message;
private String sheduleCod;
private List<Algorithm> algorithms;
in JsonDeserialize.class
public class JsonDeserialize {
private String status;
private String message;
private String sheduleCod;
private List<Algorithm> algorithms;
public JsonDeserialize(String status, String message, String sheduleCod, List<Algorithm> algorithms) {
this.status = status;
this.message = message;
this.sheduleCod = sheduleCod;
this.algorithms = algorithms;
}
..... and then getters and setters
Algorithm.class
public class Algorithm {
private String cod;
private String kcp;
private String dtBeg;
private String dtEnd;
public Algorithm(String cod, String kcp, String dtBeg, String dtEnd) {
this.cod = cod;
this.kcp = kcp;
this.dtBeg = dtBeg;
this.dtEnd = dtEnd;
}
public Algorithm () {
}
The output MethodPackage.JsonDeserialize#6580cfdd means that you print the reference and not the values of the object.
To fix this problem override the toString method within the JsonDeserialize class like the following:
#Override
public String toString() {
String values = ""; // you could also use a StringBuilder here
values += "Status: " + status + "\n";
values += "Message: " + message + "\n";
// ....
return values;
}
or use:
System.out.println(deserialize.getStatus())
System.out.println(deserialize.getMessage());
// ...
If you are using Jackson or GSON, you you can just create POJOs that match the structure of the data and it will work automatically. Also, in those pojos you can either make the names of the properties be exactly the same as the JSON has or else use the jackson annotation to let you provide the JSON property name for each object property. But mainly i don't see any getters and setters for your POJOS that you did show, and most likely you do need those. Be carefeul that you name the properties using the right 'bean naming' conventions.
Related
My service can receive several different jsons, such as:
{
"event":"conversation_started",
"context":"context information",
"user":{
"id":"01234567890A=",
"name":"John McClane",
"avatar":"http://avatar.example.com",
"country":"UK",
"language":"en",
"api_version":1
},
"subscribed":false
}
or
{
"event":"message",
"message":{
"type":"text",
"text":"a message to the service",
"location":{
"lat":12.34,
"lon":12.34
}
}
}
or several else jsons. The only field that is the same for all jsons is "event". All other fields can be different (depends on "event" value).
So the question is: how to convert those jsons to java objects (without making messy code)? The only way I know is to manually check "event" value (like json.startsWith("{\n\"event\":\"message\"") but I'm sure that there is any simple decision for doing this.
There are three ways I've done this. The first is to do what you're suggesting - parse the JSON, check the type, and create the object. Be very careful with using a String parser as you may or may not have things like new lines. Instead, do something like:
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(eventString);
String eventType = jsonNode.get("event").asText();
if( eventType.equalsIgnoreCase("conversation_started")) {
// create ConversationStarted object using something like:
ConversationStarted conversationStarted = objectMapper.readValue( eventString, ConversationStarted.class );
}
This, of course, requires all classes to have a concrete POJO to allow for deserialization.
Another way is to do what many other programming languages do and have a key/value map. There are a few ways to do this. One is with the Jackson libraries:
Map<String, Object> map = objectMapper.readValue(eventString, new TypeReference<Map<String,Object>>(){});
Map<String, Object> user = (Map<String, Object>) map.get("user");
System.out.println( "conversation started - avatar is " + user.get("avatar"));
That way you can pass around the Map and extract as needed. Note that you still need to understand the structure of the JSON but you don't need to have a POJO for it.
Lastly is a variation on the second solution. Using JSONPath you can pull out what you need directly. Again you will want to first check out which type of event you have. Something like:
if( JsonPath.read(eventString, "$.event").equals("conversation_started") ) {
String avatar = JsonPath.read(eventString, "$.user.avatar");
System.out.println("conversation started - avatar is " + avatar);
}
The last two methods require you to pull out values one at a time as shown. The first solution gives you a full object to work with. It is your call as to what works best in your environment.
UPD: If you don't want to convert JSON String to JAVA Object via declaring a POJO, you can parse it to JSONObject(com.alibaba.fastjson.JSONObject)
public class Event {
public static void main(String[] args) {
String jsonA = "{\"event\":\"conversation_started\",\"context\":\"context information\",\"user\":{\"id\":\"01234567890A=\",\"name\":\"John McClane\",\"avatar\":\"http://avatar.example.com\",\"country\":\"UK\",\"language\":\"en\",\"api_version\":1},\"subscribed\":false}";
String jsonB = "{\"event\":\"message\",\"message\":{\"type\":\"text\",\"text\":\"a message to the service\",\"location\":{\"lat\":12.34,\"lon\":12.34}}}";
JSONObject jsonObject = JSONObject.parseObject(jsonA);
String event = jsonObject.getString("event");
if (event.equals("message")) {
//do what you want to do
System.out.println("message event......");
} else if ("conversation_started".equals(event)) {
System.out.println("context information event......");
}
}
}
Declaring a class of Event as below, and then convert JSON String to a Event JAVA object.
#Data
public class Event {
private String event;
private String context;
private User user;
private boolean subscribed;
private Message message;
#Data
public static class User {
private String id;
private String name;
private String avatar;
private String country;
private String language;
private int api_version;
}
#Data
public static class Message {
private String type;
private String text;
private Location location;
#Data
public static class Location {
private double lat;
private double lon;
}
}
public static void main(String[] args) {
String jsonA = "{\"event\":\"conversation_started\",\"context\":\"context information\",\"user\":{\"id\":\"01234567890A=\",\"name\":\"John McClane\",\"avatar\":\"http://avatar.example.com\",\"country\":\"UK\",\"language\":\"en\",\"api_version\":1},\"subscribed\":false}";
String jsonB = "{\"event\":\"message\",\"message\":{\"type\":\"text\",\"text\":\"a message to the service\",\"location\":{\"lat\":12.34,\"lon\":12.34}}}";
ObjectMapper objectMapper = new ObjectMapper();
try {
Event eventA = objectMapper.readValue(jsonA, new TypeReference<Event>() {
});
System.out.println(objectMapper.writeValueAsString(eventA));
Event eventB = objectMapper.readValue(jsonB, new TypeReference<Event>() {
});
System.out.println(objectMapper.writeValueAsString(eventB));
} catch (IOException e) {
e.printStackTrace();
}
}
}
Use a JSON object. This is dynamic and can load any json. Then you can reference the event field consistently
Example 1
//import java.util.ArrayList;
//import org.bson.Document;
Document root = Document.parse("{ \"event\" : \"conversation_started\", \"context\" : \"context information\", \"user\" : { \"id\" : \"01234567890A=\", \"name\" : \"John McClane\", \"avatar\" : \"http://avatar.example.com\", \"country\" : \"UK\", \"language\" : \"en\", \"api_version\" : 1 }, \"subscribed\" : false }");
System.out.println(((String)root.get("event")));
Example 2
//import java.util.ArrayList;
//import org.bson.Document;
Document root = Document.parse("{ \"event\" : \"message\", \"message\" : { \"type\" : \"text\", \"text\" : \"a message to the service\", \"location\" : { \"lat\" : 12.34, \"lon\" : 12.34 } } }");
System.out.println(((String)root.get("event")));
Java object:
public class Foo {
#JsonProperty("name")
private String name;
#JsonProperty("surname")
private String surname;
// getters + setters
}
JSON:
{
"meta":{
"code":200
},
"data":[
{
"name":"John",
"surname":"Smith"
}
]
}
API call:
return restTemplate.getForEntity(requestUrl, Foo[].class).getBody();
Is it possible to parse "data" array without creating an additional wrapper class? I tried adding the #JsonRootName("data") annotation on top of my Java class, but it did not work.
You can try with:
import org.json.*;
JSONObject obj = new JSONObject(" .... ");
String name = obj.getJSONObject("data").getString("name");
I have a model defined to which i need to map the jsonArray.The model is as follows:
#JsonPropertyOrder({
"Command",
"Created",
"Id",
"Image",
"Labels",
"Names",
"Ports",
"Status"
})
public class Container {
#JsonProperty("Command")
private String Command;
#JsonProperty("Created")
private long Created;
#JsonProperty("Id")
private String Id;
#JsonProperty("Image")
private String Image;
#JsonProperty("Labels")
private List<String> Labels;
#JsonProperty("Names")
private List<String> Names = new ArrayList<String>();
#JsonProperty("Ports")
private List<Port> Ports = new ArrayList<Port>();
#JsonProperty("Status")
private String Status;
//Getters and Setters
}
The response is JSONArray and this is what i have to map to the Container Class which is as follows:
[
{
"Command":"/usr/sbin/sshd -D",
"Created":1429686533,
"Id":"c00afc1ae5787282fd92b3dde748d203a83308d18aaa566741bef7624798af10",
"Image":"jay8798:latest",
"Labels":{
},
"Names":[
"/jay8798"
],
"Ports":[
{
"IP":"0.0.0.0",
"PrivatePort":22,
"PublicPort":32845,
"Type":"tcp"
},
{
"IP":"0.0.0.0",
"PrivatePort":3306,
"PublicPort":32846,
"Type":"tcp"
}
],
"Status":"Up 12 hours"
},
{
"Command":"/usr/sbin/sshd -D",
"Created":1429686039,
"Id":"d70f439231d99889c1a8e96148f13a77cb9b83ecf8c9d4c691ddefa40286c04c",
"Image":"jay898:latest",
"Labels":{
},
"Names":[
"/jay898"
],
"Ports":[
{
"IP":"0.0.0.0",
"PrivatePort":22,
"PublicPort":32841,
"Type":"tcp"
},
{
"IP":"0.0.0.0",
"PrivatePort":3306,
"PublicPort":32842,
"Type":"tcp"
}
],
"Status":"Up 12 hours"
}
]
This is what I have tried but nothing seems to be working:
Container[] containerList = mapper.readValue(containerResponse, Container[].class);
int totalContainers = containerList.length;
//This will return correct length of the container.
System.out.println("This is the retrived size : " + containerList.length);
for(Container cont : containerList) {
// this statement will print 'null'.There is no exception thrown at this point.
System.out.println("Container Id : "+cont.getId());
}
Or
List<Container> containerList =
mapper.readValue(containerResponse, new TypeReference<List<MyClass>>(){});
I followed Jackson Deserialize and tried all the solutions mentioned in the post.
It is not able to map to the model.All the fields are null one it reads the values. No value is mapped to the attribute of Container class.Any thing missing in the model or do i need to write a customer logic to deserialize the json ?
First off, I'm assuming that
"Labels": {
},
should actually be
"Labels": [
],
since you've defined it to be a list of Strings and not an object of it's own.
This code works for me:
Port.java:
public class Port {
#JsonProperty("IP")
private String ip;
#JsonProperty("PrivatePort")
private int privatePort;
#JsonProperty("PublicPort")
private int publicPort;
#JsonProperty("Type")
private String type;
// Getters and setters
}
main():
String json = ... // Your JSON with the above correction
ObjectMapper objectMapper = new ObjectMapper();
List<Container> containers = objectMapper.readValue(json, new TypeReference<List<Container>>() {});
System.out.println(containers.size());
for(Container container : containers) {
System.out.println("Container Id : " + container.getId());
}
2
Container Id : c00afc1ae5787282fd92b3dde748d203a83308d18aaa566741bef7624798af10
Container Id : d70f439231d99889c1a8e96148f13a77cb9b83ecf8c9d4c691ddefa40286c04c
I'm surprised you are not getting an exception when trying to deserialize the object. When I run your code locally I get a
Exception in thread "main"
com.fasterxml.jackson.databind.JsonMappingException: Can not
deserialize instance of java.util.ArrayList out of START_OBJECT token
The reason looking like you are trying to serialize into an Array when it is a JSON object.
"Labels":{
},
That is a JSON object, so you should change your type to reflect what it is, or change the serialization of that JSON property to be an array. If you just want to change the type in your Container class, you should use a Map:
#JsonProperty("Labels")
private Map<String, Object> Labels;
I am trying to parse the response from the server something like this
public class TestClass {
public class TaskResponse {
private String id;
private List<String> links;
public String getId(){
return id;
}
}
public static void main(String args[]){
String response = "{
"task": {
"id": 10,
"links": [
{
"href": "http://localhost:9000/v1/115e4ad38aef463e8f99991baad1f809/os-hosts/svs144/onboard/10"
}
]
}
}";
JsonParser parser = new JsonParser();
JsonObject obj = parser.parse(response).getAsJsonObject();
Gson gson = new Gson();
TaskResponse tskResponse = gson.fromJson(response, TaskResponse.class);
String taskId = tskResponse.getId();
System.out.println("The task Id is "+taskId);
}
}
In what I am originally doing I get the task id as null but in this code also which I have written above to try and understand the problem, Eclipse is giving error in the string response, it does not recognize it is a proper string.
Please see to it that I can't change the response string as it is coming from server. Any help of any kind or any links to sort out the error will be highly appreciated.
Thanx in Advance.
Gson will try to match the JSON-string to your class' structure.
Since your JSON starts with task : { ... } it will try to find a property task on the class TaskResponse. Since there's no such field, it won't set anything on your instance.
So either convert it more generally (e.g. using Map.class as the target) or add the task property to a wrapper class:
class TaskResponse {
private Task task;
}
class Task {
private Long id;
private List<Link> links;
}
class Link {
String href;
}
There's also a third option detailed in this answer.
inside the string " can not directly be use
u need to escape it by replacing it to \"
" ===> \"
String response = "{
\"task\": {
\"id\": 10,
\"links\": [
{
\"href\": \"http://`localhost`:9000/v1/115e4ad38aef463e8f99991baad1f809/os-hosts/svs144/onboard/10\"
}
]
}
}";
suppose I've got a collection of people defined like this in JSON.
{
"NOM": "Doe",
"PRENOM": "John",
"EMAIL": "john.doe#email.me",
"VILLE": "Somewhere",
"LIKE1": "Lolcats",
"LIKE2": "Loldogs",
"LIKE3": "Lolwut",
"HATE1": "Bad stuff",
"HATE2": "Bad bad stuff"
}
Is it possible to write a JsonDeserializer that will aggregate and transform LIKE* and HATE* fields into a collection of Liking, set as a property of Person? (Note that there are only LIKE1, LIKE2, LIKE3, HATE1, HATE2.)
The final result properties would be something like:
public class Person {
private final String lastName;
private final String firstName;
private final String email;
private final String town;
private final Collection<Liking> likings;
// c-tor, getters
}
I've already the logic that can deserialize a given LIKE*/HATE* property into a Liking object but I fail to understand to aggregate and add them to a Person liking attribute.
Thx in advance!
It would have been nice if you had some code that showed you began the process of solving this problem yourself. But, here is a sample custom deserializer that does pretty much what you're looking for:
class PersonDeserializer extends JsonDeserializer<Person> {
#Override
public Person deserialize(final JsonParser parser,
final DeserializationContext content) throws IOException,
JsonProcessingException {
final ObjectCodec codec = parser.getCodec();
final JsonNode node = codec.readTree(parser);
final Person person = new Person();
final Iterator<String> fieldNameIter = node.getFieldNames();
while (fieldNameIter.hasNext()) {
final String fieldName = fieldNameIter.next();
if (fieldName.equalsIgnoreCase("EMAIL")) {
person.setEmail(node.get(fieldName).getTextValue());
} else if (fieldName.equalsIgnoreCase("NOM")) {
person.setFirstName(node.get(fieldName).getTextValue());
} else if (fieldName.equalsIgnoreCase("PRENOM")) {
person.setLastName(node.get(fieldName).getTextValue());
} else if (fieldName.equalsIgnoreCase("VILLE")) {
person.setTown(node.get(fieldName).getTextValue());
} else if (fieldName.startsWith("LIKE")) {
person.addLike(Liking.LikingType.LIKE, node.get(fieldName)
.getTextValue());
} else if (fieldName.startsWith("HATE")) {
person.addLike(Liking.LikingType.HATE, node.get(fieldName)
.getTextValue());
}
}
return person;
}
}
It presumes a Liking object similar to this:
public class Liking {
public static enum LikingType {
LIKE, HATE;
}
private LikingType type;
private String value;
// Constructors, getters/setters
}
And some changes to your Person object which I think you can figure out. If you intend to serialize the object to JSON in the same custom format then you will have to write a corresponding JsonSerializer.
Another option, not quite as robust, would be too simply use a map to store the likes and dislikes exactly as is. This solution would omit any explicit mappings for likes/dislikes and utilize the #JsonAny annotation to capture them. In this scheme the Person object would look like this:
public class Person {
private String lastName;
private String firstName;
private String email;
private String town;
#JsonAny
private Map<String, Object> otherProperties;
// Constructors, getters/setters
}
Deserializing your JSON into this modified version of Person will place all unrecognized properties into the hash map, as key-value pairs.
I'm pretty sure you can't do it the way you intend, how about doing it like this:
{
"NOM": "Doe",
"PRENOM": "John",
"EMAIL": "john.doe#email.me",
"VILLE": "Somewhere",
"likings": ["Lolcats", "Loldogs", "LIKE3": "Lolwut", "Bad stuff", "Bad bad stuff" ]
}