I'm not sure how to deserialize array containing plain strings.I'm trying to parse the following JSON
{
"state":"RT",
"testMethod":"electronic",
"testElements":[
{
"testId":[
"UT_ITXref",
"Fed_ITXref"
]
},
"testStartDate",
"testEndDate",
"testDueDate"
]
}
I'm getting the following error:
com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.test.rules.model.TestElements: no String-argument constructor/factory method to deserialize from String value ('testStartDate')
at [Source: {"state":"RT","testMethod":"electronic","testElements":[{"testId":["UT_ITXref","Fed_ITXref"]},"testStartDate","testEndDate","testDueDate"}]}; line: 1, column: 247] (through reference chain: com.test.rules.model.TestRules["testElements"]->java.lang.Object[][1])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:270)
at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:1456)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1012)
at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:370)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:315)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1282)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:159)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:150)
at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:196)
at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:20)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:499)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:511)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:396)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1198)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:314)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:148)
at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:1626)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1220)
Here is what I did , I used #JsonCreator annotation to deserialize
public class TestRules {
private String state;
private String testMethod;
private TestElements[] testElements;
#JsonCreator
public TaxRules(
#JsonProperty("state") String state,
#JsonProperty("testMethod") String testMethod,
#JsonProperty("testElements") TestElements[] testElements
) {
this.state = state;
this.testMethod = testMethod;
this.testElements = testElements;
}
}
public class TestElements {
private List<String> testId;
private List<String> elements;
public List<String> getElements() {
return elements;
}
public void setElements(List<String> elements) {
this.elements = elements;
}
public List<String> getTestId() {
return testId;
}
public void setTestId(List<String> testId) {
this.testId = testId;
}
}
Should I write custom deserializer or Is there any way that I can use the jackson API for this. Any suggestions would be appreciated.
Actually errors tells something.
JSON parser found that for testElements property there is an Array of Objects, but your Json file has mixed content.
first element is an object (I assume it is TestElement class). Then parser creates that object with empty constructor and calls appropriate setters for its properties.
but...
second,third and forth elements are String, so error says that parser tries to find constrictor with String as argument.
So, you may try to make that constructor in TestElement class and see will it work or not...
Do not forget to keep empty constructor as well.
I cannot guarantee it will work but, at least error says that.
BTW are you sure your Json object is correct? but not something like that?
{
"state":"RT",
"testMethod":"electronic",
"testElements":[
{
"testId":[
"UT_ITXref",
"Fed_ITXref"
]
}],
"testStartDate":"01-01-2017",
"testEndDate":"01-02-2017",
"testDueDate":"01-03-2017"
}
I'm a little confused because StartDate, EndDate, DueDate semantically look more like test attributes, not as elements in testElements array
{
"state": "RT",
"testMethod": "electronic",
"testElements": [
{
"testId": [
"UT_ITXref", // <-- this object is deserialized just fine
"Fed_ITXref"
]
},
"testStartDate", // <-- this is where the error is happening
"testEndDate",
"testDueDate"
]
}
Did you intend the json to be interpreted as if it looked like the following?
{
"state": "RT",
"testMethod": "electronic",
"testElements": [
{
"testId": [
"UT_ITXref",
"Fed_ITXref"
]
},
{
testId: [
"testStartDate"
]
},
{
testId: [
"testEndDate"
]
},
{
testId: [
"testDueDate"
]
}
]
}
If so, you'll need to make a custom deserializer to detect whether the element in the array is an object or a string. If it's a string, you'll probably want to construct the TestElement yourself.
Related
I have the following json file:
{
"authors":
[{"id":"author7",
"book":[
[
{
"value":{"pages":123}}]]},
{
"id": "author3",
"book": [
[
{
"value": {
"title": "LOTR"
}
},
{
"value": {
"boolean": false
}
},
],
[
{
"value": {
"pages": 350
}
},
{
"value": {
"boolean": false
}
},
],
[
{
"value": {
"boolean": false
}
},
{
"value": {
"pages": 150
}
},
]
]
},
}
I want to be able to create an object of Author but I am having problems while mapping the Json file with the Java classes I have created.
I understand that, while mapping the json file with the java classes, Authors class should have as fields
public class Authors{
private String authorId;
private Book book;
}
Class Book should be like this
public class Book {
private List<Values> values
public Book() {
}
}
But what about class Values?
public class Values{
private int pages;
private Boolean bool;
private String title;
public Values() {
}
}
Is this the correct way to map it? Because I see that if I create an object of Values it will ask me to modify the constructor or create a new constructor for each different object that comes from Json
Thank you for reading and helping!
Easy way out would to be to define Values by type 'object' as,
public class Book {
private List<Object> values
}
This way you won't run into the issue of having properties with null values when they don't exist in the JSON. Also, the JSON would be entirely parsed into an object even if new properties are get introduced (or which may not be defined under values class, as you have done above causing those not being mapped to the object).
However, when you are using 'object', be mindful with your logic that uses this parsed object. Although, you are assured to access the properties from the object as in the JSON, you will have to conditionally check whether the nessacary property exists first in each logical context to avoid possible undefined value errors.
I have a nested JSON that I want to loop through and get value based on key.
Data.json
{
"car": [
{
"date": 1324599600000,
"values": [
{
"name": "Audi",
"price": "11212.12"
},
{
"name": "Bmw",
"price": "22321.3"
},
{
"name": "Cittroen",
"price": "23432.2"
},
{
"name": "Tuareg",
"price": "556456.3"
}
]
}
I created 3 object models based on JSON data.
CarResponse.java
public class CarResponse {
#JsonProperty(value = "car")
List<Car> cars;
//getters,setters
Car.java
public class Car{
#JsonProperty("values")
private List<CarValue> carValue;
//getters, setters
CarValue.java
public class CarValue {
private String name;
private BigDecimal price;
//getters, setters
DataParse.java
public class DataParse{
CarResponse response;
public CarValue parse(){
CarValue carValue = new CarValue();
//NEED HELP WITH THIS PART
for(Car cars : response.getQuote()){
for(CarValue qv : cars.getCarValue()){
String type = qv.get("name").asText();
Decimal value = qv.get("price").decimalValue();
carValue.setName(qv.getName());
carValue.setPrice(qv.getPrice());
}
}
return quoteValue;
}
}
In JSON I need to check if car name is Audi and if car name is Tuareg then save it and display it.
Nested for loop part of code is not working
for(Car cars : response.getQuote()){
for(CarValue qv : cars.getCarValue()){
String type = qv.get("name").asText();
Decimal value = qv.get("price").decimalValue();
carValue.setName(qv.getName());
carValue.setPrice(qv.getPrice());
}
}
Just put it as an example of what I need to do in this step which is check for key in JSON and if it is that key with for example name = Audi then
carValue.setName(qv.getName());
With my code above I am getting this in the console.
carValue=[carValue{name=null, price=null}
Assuming that your deserialization is working correctly, you should change the part that "is not working" to:
String type = qv.getName();
BigDecimal value = qv.getPrice();
If for some reason you want to use this qv.get(key) method, you should post its implementation.
When getName and getPrice are still returning null, it means you have a problem with the deserialization, for example a wrong setter method name. Still, without the implementation is impossible to understand.
Here is the official documentation of Jackson, the library used by default with spring-boot for JSON serialization / deserialization.
And here is Spring blog post about it.
I am trying to parse a polymorphic object (it is from the Strava api) with a jackson object mapper. The object looks like this:
[
{
"type": "latlng",
"data": [
[ 38.603734, -122.864112 ],
[ 38.608798, -122.867714 ],
... omitted for brevity ...
[ 38.611205, -122.870848 ],
[ 38.603579, -122.863891 ]
],
"series_type": "distance",
"original_size": 512,
"resolution": "low"
},
{
"type": "distance",
"data": [
0.0,
1305.8,
... omitted for brevity ...
128136.6,
129444.1
],
"series_type": "distance",
"original_size": 512,
"resolution": "low"
}
]
So based on the type the field data has a different object in it. In most cases it is an array of floats. In the case of the "latlng", there is an array of float[], so it is a float[][] (I would think).
I create an object that represents this data structure, with a deserializer. It looks like this:
public class StravaStream {
#JsonProperty("type")
private String type;
public String getType() {
return type;
}
public static class StravaStream1D extends StravaStream {
#JsonProperty("data")
private float[] data;
public StravaStream1D() {
}
public float[] getData() {
return data;
}
}
public static class StravaStream2D extends StravaStream {
#JsonProperty("data")
private float[][] data;
public StravaStream2D() {
}
public float[][] getData() {
return data;
}
}
public StravaStream() {
}
public static class StravaStreamDeserializer extends StdDeserializer<StravaStream> {
public StravaStreamDeserializer() {
super(StravaStream.class);
}
#Override
public StravaStream deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
Class<? extends StravaStream> variantStravaStream;
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
ObjectNode root = (ObjectNode) mapper.readTree(jp);
JsonNode type = root.get("type");
System.out.println("type is "+type);
if (type.textValue().equals("latlng")) {
variantStravaStream = StravaStream2D.class;
} else {
variantStravaStream = StravaStream1D.class;
}
System.out.println("variant is "+variantStravaStream.getSimpleName());
return mapper.readValue(jp, variantStravaStream);
}
}
}
When I only ask for one dimensional data, like distance objects or so, it works out fine. But when I try to parse the "latlng" float[][], jackson fails. I am sure that the type is recognized, see the additonal system.out, it prints that a StravaStream2D.class variant is used.
The error message (and the additional system out) I get is:
01-26 09:05:49.605 27165-27165/nl.jfvh.stravatest I/System.out: type is "latlng"
01-26 09:05:49.605 27165-27165/nl.jfvh.stravatest I/System.out: variant is StravaStream2D
01-26 09:05:49.620 27165-27165/nl.jfvh.stravatest W/System.err: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of float[] out of VALUE_NUMBER_FLOAT token
01-26 09:05:49.620 27165-27165/nl.jfvh.stravatest W/System.err: at [Source: java.io.StringReader#e8550ec; line: 1, column: 40164] (through reference chain: java.util.ArrayList[0]->nl.jfvh.stravatest.client.model.StravaStream2D["data"]->[Ljava.lang.Object[][0])
Is my data model wrong? The parsing of polymorphic objects is very new for me, I followed some tutorials, but the problems seems to be in the simple mapping of the data on the float[][]...
Since (according to you) you are new to parsing polymorphic objects, I'd suggest using annotations instead of a custom deserialiser. It's as simple as:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true)
#JsonSubTypes({
#JsonSubTypes.Type(name = "latlng", value = StravaStream.StravaStream2D.class),
#JsonSubTypes.Type(name = "distance", value = StravaStream.StravaStream1D.class)
})
public class StravaStream {
Read docs for JsonTypeInfo and JsonSubTypes
An issue with the custom deserialiser you're trying with is that when you do return mapper.readValue(jp, variantStravaStream); at the end it will actually re-enter the deserializer because class StravaStream2D extends StravaStream so Jackson will use the custom deserializer again. I got stack overflow errors when trying this due to infinite recursion.
The JSON I'm parsing looks like this:
{ "version": 1
"data": {
"1001": {
"id": 1001,
"name": "herp",
"into": [
"3111": "we"
]
},
"1032": {
"id": 1002,
"name": "derp",
"into": [
"36": "w",
"12341: "c"
],
"tags": [
"hi there"
],
"cost" {
"even": 15
}
},
"1603": {
"id": 1003,
"name": "her",
"into": [
"37": "dll",
"58": "eow",
"32145": "3a"
],
"cost" {
"highest": 325
"lowest": 100
}
},
.... Even more data
}
The Json that is within "data" goes on for a while and does not have a set endpoint. I have no control over the Json, I'm just trying to read it. Unfortunately, with my class code I'm unable to get it to work. When I make a retrofit call the information inside "data" is empty.
I've tried many iterations of implementing this, including using a deserializer and restructuring my POJO code. This is the current state of my Data class:
public class Data {
private Map<String, Item> itemData;
// Relevant Getters, Setters and Constructors //
}
For my Item Class, the main issue is that the JSON Content isn't set, it can vary at times. As you can see above the values inside "into" vary and sometimes the amount of things within item changes as well such as "tags" or "cost":
public class Item {
private int id;
private String name;
private String group;
private String description;
private Map<String, String> into;
private List<String> tags;
private Map<String, Integer> cost;
// Relevant Getters, Setters and Constructors //
When I use this code my data class is empty, I don't see any errors in the log so I can't seem to figure out why the GSON isn't working with this.
In case you wanted to see how I construct my RestClient, here it is:
RestAdapter.Builder builder = new RestAdapter.Builder()
.setEndpoint(ROOT)
.setClient(new OkClient(new OkHttpClient()))
.setLogLevel(RestAdapter.LogLevel.FULL);
RestAdapter restAdapter = builder.build();
REST_CLIENT = restAdapter.create(DataApi.class);
I know that my Rest Query works because I can get the content within "version" but everything inside data is null.
I am officially a dumb scrub and completely looked over the easiest possible fix and should have my account and developer title revoked.
This is all I should have done:
public class ItemGroup {
private String version;
private Map<String,Item> data;
//...Man i'm so dumb...
}
AS REFERENCE FOR THE FUTURE. The reason why this works is because the JSON is in this format { { } { } { } }. Which means you have 3 objects of objects, as opposed to { [ ] [ ] [ ] } which is 3 objects of a list. What I had done was treat { { } { } { } } as { { { } { } { } } }. Which is not correct. By using a map which is basically a collection of pairs, we are able to imitate the { { } { } { } } with a Map.
Map Object { {Key-Pair Object} {Key-Pair Object} {Key-Pair Object} }
Just a quick idea for your Pojos:
Not sure if this will work. Maybe write a custom deserializer.
public class YourResponse {
int version;
Data data;
class Data {
Subdata subData;
}
class Subdata {
private int id;
private String name;
private ArrayList<Into> into;
private ArrayList<String> tags;
//...
}
class Into {
// "3111": "we"
private String into;
}
}
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;