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
Related
I have a java class as mentioned below, In that, I want to create an inner JSON using duration & durationType while serialization using Jackson.
public class Task {
private Long id;
private String name;
private Integer duration;
private String durationType
// getters & setters...
If I serialize (POJO to JSON String) I need output as following, without creating a duration class.
{
"id" : 1001,
"name" : "Task title"
"duration" : {
"value" : 4
"type" : "hours"
}
}
If it is that possible, please let me know. Thanks in advance :)
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.
i have a json string like this
{
"code": "200",
"data": "",
"datadetail": null,
"message": "Khách hàng không tồn tại",
"description": "KH_NOTFOUND:"
}
because the value of data property is "" so that the object mapper can't not map that field in to Java POJO, is that anyway to modify the value of data property to specific string like below
{
"code": "200",
"data": "no data",
"datadetail": null,
"message": "Khách hàng không tồn tại",
"description": "KH_NOTFOUND:"
}
here is the error
detail
Can not instantiate value of type [collection type; class java.util.ArrayList, contains [simple type, class vnptpay.collection.adapter.partner.water.laichau.Datum]] from String value (''); no single-String constructor/factory method
here is my target java object class to map
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"code",
"data",
"datadetail",
"message",
"description"
})
#JsonIgnoreProperties(ignoreUnknown = true)
public class GetBillResponse implements Serializable{
#JsonProperty("code")
private String code;
#JsonProperty("data")
private ArrayList<Datum> data = null;
#JsonProperty("datadetail")
private Object datadetail;
#JsonProperty("message")
private String message;
#JsonProperty("description")
private String description;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
here is my mapping code
ObjectMapper mapper = new ObjectMapper();
GetBillResponse obj = null;
obj = mapper.readValue(output.toString(), GetBillResponse.class);
Assuming output.toString() is your JSON string you could add .replace("\"\"", \"{none}\"). Java should be able to interpret this as an ArrayList with one element (maybe my syntax is wrong, but there is definitely a way to initialise an ArraList from a String).
Edit: Now that I think about it, you probably need JSON-syntax, not Java-syntax, so .replace("\"\"", \"[]\") or something like that.
As said in the comments, you're mapping from a wrong type.
In your JSON "data": "" refers to a String field name data. When the java code is mapping it, as the default value passed is a string, it tries to look for an ArrayList constructors that takes a String as you defined it as list in your Java code:
#JsonProperty("data")
private ArrayList<Datum> data = null;
So you have 2 options now:
you send a valid JSON array that will be mapped to the list like "data": []. (assuming you have a constructor like Datum(String value))
You change the data mapping in your java code to a String and map it to an ArrayList later (if for instances your splitting the String on commas or so)
Also for the record I wouldn't advise you yo map data to an ArrayList but rather map it to a List as it is common best practice to refer to interfaces and not implementations !
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.
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.