desrialize JSON with child array using Jackson annotations? - java

I'm trying to parse some JSON containing a nested array. I'd like the array to map to a list of child objects within the parent I'm mapping. Here is the (slightly abbreviated) JSON and Java classes
JSON:
{
"id": "12121212121",
"title": "Test Object",
"media$content": [
{
"plfile$audioChannels": 1,
"plfile$audioSampleRate": 18000,
},
{
"plfile$audioChannels": 2,
"plfile$audioSampleRate": 48000,
},
{
"plfile$audioChannels": 2,
"plfile$audioSampleRate": 48000,
}
]
}
Java classes
class MediaObject {
#JsonProperty("id")
private String id;
#JsonProperty("title")
private String title;
#JsonProperty("media$Content")
private List<MediaContent> mediaContent;
... getters/setters ...
}
class MediaContent {
#JsonProperty("plfile$audioChannels")
private int audioChannels;
#JsonProperty("plfile$audioSampleRate")
private int audioSampleRate;
... getters/setters ...
}
I'd like to be able to deserialize using annotations along with the standard mapper code, i.e.
mapper.readValue(jsonString, MediaObject.class)
Everything works fine with the "id" and "title" fields, but my list of MediaContent objects always comes up null. This seems like something Jackson should be able to handle without much trouble, can anyone see what I'm doing wrong here?

The name of the json field is wrong - the attribute is not media$Content, rather media$[c]ontent. Otherwise I do not see why it will not work.

Related

smart mapstruct mapping from JSON to DTO

i am working on a spring application and using Spring Webclient to make REST call to an external service. as a result, i have this object :
{
"error": 0,
"message": "",
"totalItems": 1,
"entities": [
[
{
"entityName": "Field 1",
"entityValue": "String value 1"
},
{
"entityName": "Field 2",
"entityValue": "Date Value 2 in dd-mm-yyyy format"
}
]
]
}
now i would like to create a DTO that contains two fields (String field1 and Date field2) and tell mapstruct to populate field1 with the entityValue corresponding to entityName Field 1 and same thing for field 2. how can i achieve that in a smart way ? ideally without looping on all entityName/entityValue pairs in each entity.
for now i have two DTOs representing my REST result :
public class Wrapper {
private int error;
private String message;
private int totalItems;
private List<PairEntity> entities;
}
and
public class PairEntity {
private String entityName;
private String entityValue;
}
A little looping is included...
if entityName is unique, change your dto to use a Map<String, String> or Map<String, PairEntity> (key = entityName) instead of List<PairEntity>. You'll need a custom jackson mapper or gson, depending which lib is in use...
Or create an new dto and convert the list to a map manually.
Then create a custom mapping in mapstruct and get the values from the map.
https://www.baeldung.com/java-list-to-map
https://www.baeldung.com/jackson-deserialization
https://www.baeldung.com/mapstruct-custom-mapper

Is there existing functionality to search through a JSON object and replace values

Is there a way we could check a json object for empty strings for each of its value and replace it as null?
Looking for any existing solution that might exist in Spring preferably. I could of course loop over these and check then one by one and replace empty strings with null but prefer not to. This same object is used in other places where I will not be doing this replacement thus I don't want to change this. Thus I can't take the route of #JsonSerialize and writing a custom JsonSerializer. Instead is there some existing functionality where I could pass in my object which gets validated / allows me to set empty strings to null? Please advice how I could achieve this. Thanks.
Object Structure
{
"name": "a",
"id": 1,
"stuff": {
"name": "b",
"color": "red"
},
"others": [{
"name": "" // For example since this is empty, I want to replace this as null.
},
{
"name": " item2"
},
{
"name": " item3"
},
{
"name": " item4"
}
]
}
Object Classes. Trying to find a solution to be able to just pass in the Response object.
public class Other {
private String name;
}
public class Response {
private String name;
private Integer id;
private Stuff stuff;
private List<Other> others;
}
public class Stuff {
public String name;
public String color;
}
You could use different JsonSerializers as per requirements.
If empty strings needed to be null in one place, Have such serializer customized accordingly.
Libraries such as Objectmapper supports this easily.

Changing JSON array to to a pojo object array

I am getting API response on the following format. I am working on Android project
{
Course: [
{
"Year": 2017,
"CourseTitle": "..",
"Quarter": "autumn",
},
{
"Year": 2017,
"CourseTitle": "..",
"Quarter": "autumn",
}
],
Instructor: {
"name": ,
"address":
}
}
My goal is to get Course array of courses, and assign it to the POJO (plain old java object) that I created using online tool. I am also using the GSON library. I did find different implementation online, and I finally used this, and I was able to filter the nested object "Courses", which holds an array object of courses.
JsonParser parser = new JsonParser();
JsonObject element = (JsonObject)parser.parse(response);
JsonElement responseWrapper = element.getAsJsonArray("Courses");
When I look responseWrapper using debugger tool, it holds a JSON array of course (objects). I wanted to assign these data to the "Course" POJO class I created
public class Course implements Parcelable {
private String href;
private int year;
private String courseTitle;
private String quarter;
private String courseTitleLong;
private String curriculumAbbreviation;
private String courseNumber;
}
I added the following line.
Course[] courseList = gson.fromJson(responseWrapper,Course[].class);
CourseList is an array of Course object, but when I look what I got using debugger tool all the field variables are "null". How could I solve this problem? Is there a better way I should have approached? The whole purpose is getting array of course objects so I could manipulate it for display.
Try to rename your fields to exactly match the json response, even case-wise.

GSON How to deserialize to a class with this array json string

I am struggling to find a way to serialize / deserialize this JSON output to a Java class? Can anyone provide code sample?
[
{
"topic": "this is my topic"
},
[
{
"name": "John"
},
{
"age": 100
}
]
]
My current attempt uses this Javabean:
public class Test {
private String topic;
private List<Person> listOfPersons;
}
Which I try to deserialize data into using Gson:
gson.fromJson(this.json, Test[].class);
But the deserialization fails, because Gson is looking for a list of persons in the JSON, but it doesn't exist.
It doesn't seem like having an object next to an array, inside an array, is sensical. It would make sense to put things this way:
{
"topic": "this is my topic",
"listOfPersons" : [
{
"name": "John",
"age": 100
},
{
... another person
}
]
}
Then you could just have a Person class:
public class Person {
private String name;
private int age;
}
...and you could deserialize with the code you already have.
The problem here is that your JSON data is syntactically correct, but semantically ambiguous. And by that I mean, it appears to represent a polymorphic array, where each item in the array is of a different type.
In addition, the portion representing a 'person' seems to have been de-normalized horribly, where each attribute of a person is represented as a separate object in an array. Quite weird indeed. Unfortunately its really impossible to tell what types are represented just by looking at the data alone, and there are no hints provided to allow Gson to deserialize the data for you. The best you can do in this case is manually parse the information.
Test test = new Test();
JsonArray rootArray = new JsonParser().parse(jsonString);
test.setTopic(rootArray.get(0).get("topic");
Person person = new Person();
JsonArray personArray = rootArray.get(1);
person.setName(personArray.get(0).get("name"));
person.setAge(personArray.get(1).get("age"));
test.setListOfPersons(Arrays.asList(person));

Configure XStream to dynamically map to different objects

I am hitting a RESTful 3rd party API that always sends JSON in the following format:
{
"response": {
...
}
}
Where ... is the response object that needs to be mapped back to a Java POJO. For instance, sometimes the JSON will contain data that should be mapped back to a Fruit POJO:
{
"response": {
"type": "orange",
"shape": "round"
}
}
...and sometimes the JSON will contain data that should be mapped back to an Employee POJO:
{
"response": {
"name": "John Smith",
"employee_ID": "12345",
"isSupervisor": "true",
"jobTitle": "Chief Burninator"
}
}
So depending on the RESTful API call, we need these two JSON results mapped back to one of the two:
public class Fruit {
private String type;
private String shape;
// Getters & setters for all properties
}
public class Employee {
private String name;
private Integer employeeId;
private Boolean isSupervisor;
private String jobTitle;
// Getters & setters for all properties
}
Unfortunately, I cannot change the fact that this 3rd party REST service always sends back a { "response": { ... } } JSON result. But I still need a way to configure a mapper to dynamically map such a response back to either a Fruit or an Employee.
First, I tried Jackson with limited success, but it wasn't as configurable as I wanted it to be. So now I am trying to use XStream with its JettisonMappedXmlDriver for mapping JSON back to POJOs. Here's the prototype code I have:
public static void main(String[] args) {
XStream xs = new XStream(new JettisonMappedXmlDriver());
xs.alias("response", Fruit.class);
xs.alias("response", Employee.class);
// When XStream sees "employee_ID" in the JSON, replace it with
// "employeeID" to match the field on the POJO.
xs.aliasField("employeeID", Employee.class, "employee_ID");
// Hits 3rd party RESTful API and returns the "*fruit version*" of the JSON.
String json = externalService.getFruit();
Fruit fruit = (Fruit)xs.fromXML(json);
}
Unfortunately when I run this I get an exception, because I have xs.alias("response", ...) mapping response to 2 different Java objects:
Caused by: com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$UnknownFieldException: No such field me.myorg.myapp.domain.Employee.type
---- Debugging information ----
field : type
class : me.myorg.myapp.domain.Employee
required-type : me.myorg.myapp.domain.Employee
converter-type : com.thoughtworks.xstream.converters.reflection.ReflectionConverter
path : /response/type
line number : -1
version : null
-------------------------------
So I ask: what can I do to circumvent the fact that the API will always send back the same "wrapper" response JSON object? The only thing I can think of is first doing a String-replace like so:
String json = externalService.getFruit();
json = json.replaceAll("response", "fruit");
...
But this seems like an ugly hack. Does XStream (or another mapping framework) provide anything that would help me out in this particular case? Thansk in advance.
There are two ways with Jackson:
test manually that the wanted keys are there (JsonNode has the necessary methods);
use JSON Schema; there is one API in Java: json-schema-validator (yes, that is mine), which uses Jackson.
Write a schema matching your first object type:
{
"type": "object",
"properties": {
"type": {
"type": "string",
"required": true
},
"shape": {
"type": "string",
"required": true
}
},
"additionalProperties": false
}
Load this as a schema, validate your input against it: if it validates, you know you need to deserialize against your fruit class. Otherwise, make the schema for the second item type, validate against it as a security measure, and deserialize using the other class.
There are code examples for the API, too (version 1.4.x)
If you do know the actual type, it should be relatively straight-forward with Jackson.
You need to use a generic wrapper type like:
public class Wrapper<T> {
public T response;
}
and then the only trick is to construct type object to let Jackson know what T there is.
If it is statically available, you just do:
Wrapper<Fruit> wrapped = mapper.readValue(input, new TypeReference<Wrapper<Fruit>>() { });
Fruit fruit = wrapped.response;
but if it is more dynamically generated, something like:
Class<?> rawType = ... ; // determined using whatever logic is needed
JavaType actualType = mapper.getTypeFactory().constructGenericType(Wrapper.class, rawType);
Wrapper<?> wrapper = mapper.readValue(input, actualType);
Object value = wrapper.response;
but either way it "should just work". Note that in latter case you may be able to use base types ("? extends MyBaseType"), but in general dynamic type can't be specified.

Categories

Resources