I have to work with an API that returns all objects wrapped in a unnamed root object. Something like this:
{
"user": {
"firstname":"Tom",
"lastname":"Riddle"
}
}
Here, I am interested in deserializing the user object only. But given the nature of the response, I will have to write a class that wraps the user object if I want to deserialize it successfully.
#Getter
#Setter
#ToString
// Wrapper class
public class Info {
private User user;
}
and then
#Getter
#Setter
#ToString
public class User {
private String firstname;
private String lastname;
}
All responses of the API return the response in this manner, so I am looking for a way to deserialize the response in such a way as to have one generic wrapper class that can be used to extract any type of JSON object.
I have tried this:
#Getter
#Setter
public class ResponseWrapper<T> {
private T responseBody;
}
and then
ResponseWrapper<User> userInfo = objectMapper.readValue(response.body().string(), ResponseWrapper.class);
But this results in the following exception:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "user" (class com.redacted.response.ResponseWrapper), not marked as ignorable (one known property: "responseBody"])
So, is there any way for me to deserialize this response without having to write separate wrapper classes for each API response like this?
You can do something like this:
JsonNode jsonNode = objectMapper.readTree(response.body().string());
String content = jsonNode.elements().next().toString();
User user = objectMapper.readValue(content, User.class);
Output:
User(firstname=Tom, lastname=Riddle)
Related
I'm having a problem with deserializing json object into DTO class.
The dto object has the following structure:
#Getter
#Setter
#ToString
#RequiredArgsConstructor
#JsonIgnoreProperties(value = { "charsetinfo" })
public class SingleEngineJsonDto {
public List<SingleEngineDTO> engine;
public List<Timetable> timetable;
public List<Dailytable> dailytable;
}
And the output is:
[SingleEngineJsonDto(engine=null, timetable=null, dailytable=null),
SingleEngineJsonDto(engine=[SingleEngineDTO(name=state, … some data)],
timetable=[Timetable(weekDay=1,some data), more data],
dailytable=[Dailytable(date=2018-05-09, more data), more data])]
How do I get rid of this([SingleEngineJsonDto(engine=null, timetable=null, dailytable=null)) part? Original json contains metadata, that I don't need. The firs object in a big json object is ignored metadata and null fields, the second one is null fields filled in. The only idea that I have is list.get(1). And I was told that this solution is wrong.
UPDATE:
Original json structure:
[{"charsetinfo":{"name": "utf-8"}},{"engine":
[{"NAME": "stock","title": some data}],
"timetable":[{"week_day":1,"is_work_day":1,some data},
more data],"dailytable":[{"date": "2018-05-09","is_work_day":0,"start_time": "10:00:00",data}]}]
Desirialization:
#FeignClient(value = "engine", url = "engine")
public interface EngineClient {
#GetMapping
List<EngineJsonDto> getEngines(URI enginesUri,
#RequestParam(value = "lang", required = false) String lang);
#GetMapping
List<SingleEngineJsonDto> getEngine(URI engineUri, #RequestParam(value = "lang", required = false) String lang);
}
Service gets the data from client and gets a list of dto to work with. I use facory pattern to get pieces of data to work with parameters(engine,timetable,dailytable), and all of them had to look the same:
#Service
public class EngineParamEngine implements EngineFactoryInterface {
#Override
public List<SingleEngineDTO> getEngineObjectPart(List<SingleEngineJsonDto> list){
return list.get(1).getEngine(); //<<this
}
}
I was told to change it to list.get(0).Which gives me (engine=null, timetable=null, dailytable=null). So, I need to make the first object disappear somehow. I'm sorry, English is not my first language and I'm new to programming.
I'm trying to deserialize a response of either an Object or a List of Objects from our southbound API. Now, I tried just parsing an object or a List of Objects with this code just removing either of the two and it works. So I'm sure either of the parsing I have below works, but when I am trying to combine both parsers into one, I get an error of
Cannot deserialize instance of object out of START_ARRAY token
This is even though the payload with object is correctly formatted and this code below could convert a single object to a List of Object and vice versa.
Below is the code that I have.
#Data
#AllArgsConstructor
#NoArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class)
// #JsonInclude(JsonInclude.Include.NON_NULL)
public final class GetCreditCardInfo {
// Deserializer for List of Objects
#JsonAlias("ns:return")
private List<CreditCardDetails> details;
// Deserializer for an Object
#JsonAlias("ns:return")
private CreditCardDetails detailsObject;
public List<CreditCardDetails> getDetails() {
// List< CreditCardDetails > list = List.of( detailsObject ) ;
if(details == null) {
details = List.of(detailsObject);
} else {
}
return details;
}
}
What the code does above is that i tries to parse either an object or a list of object. If it cant get a list of object, it would then try to parse the object into a list of object, otherwise, it would retain the list of object it got from the southbound API.
What do I do to make this work as I intended?. Thanks
Sample JSON List of Objects
{
"soapenv:Envelope":{
"soapenv:Body":{
"ns:getCreditCardResponse":{
"xmlns:ns":"",
"ns:return":[
{
"ax21:cif":"1"
},
{
"ax21:cif":"2"
}
],
"xmlns:ax21":"",
"xmlns:ax23":""
}
},
"xmlns:soapenv":"http://schemas.xmlsoap.org/soap/envelope/"
}
}
Sample JSON Object:
{
"soapenv:Envelope":{
"soapenv:Body":{
"ns:getCreditCardResponse":{
"xmlns:ns":"",
"ns:return":{
"ax21:cif":"1"
},
"xmlns:cif":""
}
},
"xmlns:soapenv":"http://schemas.xmlsoap.org/soap/envelope/"
}
}
PS:
The Sample JSON Objects could already be deserialized by the two deserializers that I have in the code
I also tried annotating my class with
#JsonInclude(JsonInclude.Include.NON_NULL)
to ignore deserialization errors with non existent json field, but still the issue persist.
Sample: Object Deserializer
#JsonIgnoreProperties(ignoreUnknown = true)
#Data
#AllArgsConstructor
#NoArgsConstructor
#JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class)
public final class CreditCardDetails {
#JsonAlias("ax21:cif")
private String cif;
}
You can just add the #JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) over your private List<CreditCardDetails> details; in GetCreditCardInfo class.
This way if there is only one value (ns:return is an object) in the json, it will be deserialized as one-element List in java.
So try changing GetCreditCardInfo to the following:
#Data
#AllArgsConstructor
#NoArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class)
// #JsonInclude(JsonInclude.Include.NON_NULL)
public final class GetCreditCardInfo {
#JsonAlias("ns:return")
#JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
private List<CreditCardDetails> details;
}
Im trying to use the restTemplate.postForObject(URL, Session.class) method and map the response to a POJO. This works partially, however when i try to access an element with a name like "name-with-dashes" I cannot find the element.
The JSON I am extracting from the method call:
{"age":60,"expire":12345,"name-with-dashes":"This name has dashes?!"...}
Here is the POJO im using to extract this data:
#Getter
#Setter
#JsonIgnoreProperties(ignoreUnknown = true)
public class Session {
private int age;
private long expire;
//will not grab name-with-dashes... returns null
private String nameWithDashes;
}
You should annotate your fields, especially the ones that do not comply to bean naming conventions, with the #JsonProperty annotation as follows:
#JsonProperty("name-with-dashes")
private String nameWithDashes;
You can annotate the property
#SerializedName("name-with-dashes")
private String nameWithDashes;
using Gson
I have a simple POJO:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class StatusPojo {
private String status;
}
When I de-serialize simple string "asd" (without quotes) like this:
StatusPojo pojo = new ObjectMapper().readValue("asd", StatusPojo.class)
I am getting a StatusPojo object created successfully, with status field's value as "asd", though it is not valid JSON and nowhere has the field name "status" mentioned along.
Why is it behaving like this and how to disable this behavior and have object mapper throw an exception?
Your POJO has #AllArgsConstructor (maybe because of the #Builder) that then generates something like this:
public StatusPojo(String status) {
this.status = status;
}
When ObjectMapper then de-serializes plain string it uses that constructor to create object.
If you added some other property to your pojo, like just:
private String noAsdPlease;
there would be an exception, because ObjectMapper could not find creator, there would not be that above mentioned constructor but with two String params.
At quick glace DeserializationFeature does not have such a feature that disables using one string arg constructor for plain string.
Playing with more fields, removing #Builder & #AllArgsConstructor might resolve your problem but if you cannot change those ther might not be other options.
I have the following Object:
public class Class_a{
private List<class_b> someList;
}
public class Class_b{
private Map<String,String> someMap;
}
My json will look like this:
"someList":[{"someMap":{"strKey1":"strValue1"}},{"someMap":{"strKey2":"strValue2"}}]
Is it possible to serialize a Json that will look like this, without changing my Objects (and I will have the option to deserialize the Object):
"someList":[{"strKey1":"strValue1"},{"strKey2":"strValue2"}]
*I know that if will defined my object like this:
public class Class_a{
private List<Map<Strung,String>> someList;
}
i will get a Json like I want - but I am trying to find more elegant solution then 'list' that contain a 'map'
My project use spring framework and Jackson.
This worked for me I only had to add getters and setters to your classes and I was able to parse with jackson:
#Test
public void t() throws IOException {
String json = "{\"someList\":[{\"someMap\":{\"strKey1\":\"strValue1\"}},{\"someMap\":{\"strKey2\":\"strValue2\"}}]}";
Class_a a = new ObjectMapper().readValue(json, Class_a.class);
System.out.println(a);
}
#Getter
#Setter
public class Class_a{
private List<Class_b> someList;
}
#Getter
#Setter
public class Class_b{
private Map<String,String> someMap;
}
I'm using lombok but that's nothing special, you can create the getters/setters manually too and will work