Let's assume there are those 2 different json structures - both contain the same values, but the key hierarchy differs - one is flat, the other is nested:
Flat:
{
"documentId": "7ef1229c-301a-40d9-8c0c-faf5da54785d",
"first_name": "Xbyvuve",
"last_name": "Abmhlakwd"
}
Nested:
{
"_meta": {
"_more_nested" {
"documentId": "7ef1229c-301a-40d9-8c0c-faf5da54785d",
}
},
"first_name": "Xbyvuve",
"last_name": "Abmhlakwd"
}
Resulting in Object:
#JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class PersonDocument {
String documentId;
String firstName;
String lastName;
}
Is there a way in Jackson to instantiate the same object from both json strings without providing a custom deserializer? For instance with a special annotation? In addition, I also want the resulting json string on serialization to be like the "flat" json string.
Thank you in advance!
Tried different annotations without any positive result :<
Related
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.
My json structure is
{
"GAME_CUSTOMIZE": {
"GAME_CODE": "MOCK12V2.0",
"RESULT": {
"response": [
{
"id": "PLAYER1",
"value": "WERYT"
},
{
"id": "PLAYER2",
"value": "QEWRET"
},
{
"id": "PLYAER3",
"value": "765422"
}
]
}
}
}'
My Pojo object using for mapping is,
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonProperty("GAME_CODE")
private String gameCode;
#JsonProperty("RESULT")
private String responseJson;
When trying to assign the "RESULT" complete json to string, getting empty after conversion from json to Java object. Have tried JsonRawValue annotation also. And tried with responseJson as Object type instead of string. I want to get the RESULT Json to Java string or Object property.
The default mapping for a JSON object is a LinkedHashMap<String, Object>. The simple solution would be to change the type of responseJson to that.
Mapping to a String (containing the original JSON serialization of the object) might make sense, but I don't know if it is implementable. Mapping to java.lang.Object makes no sense.
UPDATE - the following Q&A explains how to deserialize an object as a string:
Jackson Deserialize Variable as Json String
I have a Json like below.
{
"myItemArray": [
{
"id": "c8c1",
"price": 18,
"display": {
"inneritemName1": "innerItemValue1",
"inneritemName2": "innerItemValue2",
"inneritemName3": "innerItemValue3"
}
},
{
"id": "cac1",
"price": 2,
"display": "Lemonate"
}
]
}
As you can see that the item in my array has a parameter called "display" which can be String or Object. How can I deserialize this json using Gson?
I don't want to deserialize this string manually is there any other way to to this?
You can use Gson, but have to build a class, get each Json attribute into the relevent attribute in the Java class.
I highly recomand Jackson library, you only need to setup the Java class, and it takes care of the mapping and parsing.
I am looking for a generic way to deserialise with Jackson a JSON such as:
{
"hello": "baby",
"eyes": "[blue,green]"
}
To a POJO such as
public class Whatever {
#Setter private String hello;
#Setter private List<Color> eyes;
}
With Color being an enum.
When I try naively like below:
ObjectMapper mapper = new ObjectMapper();
mapper.convertValue(properties, objectClass);
I get the error
Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token
Obviously this is because Jackson can only deserialise arrays from JSON arrays, not their string representation.
I tried to activate ACCEPT_SINGLE_VALUE_AS_ARRAY but it would consider the value of the property "eyes" to be an array with a single String element "[blue,green]" (which fails to convert to the enumeration Color)
Any hint would be very much appreciated.
The problem isn't that ACCEPT_SINGLE_VALUE_AS_ARRAY is causing the "eyes" property to be interpreted as an array with a single element, that option allows Jackson to coerce types so that
{
"hello": "baby",
"eyes": "[blue,green]"
}
would be interpreted the same as
[{
"hello": "baby",
"eyes": "[blue,green]"
}]
This way single elements can be used with Java's Collections more information can be found at
http://fasterxml.github.io/jackson-databind/javadoc/2.0.0/com/fasterxml/jackson/databind/DeserializationFeature.html#ACCEPT_SINGLE_VALUE_AS_ARRAY
As far as your problem goes, the best option would be to have the JSON submitted with color as a JSON array like:
{
"hello": "baby",
"eyes": [
"blue",
"green",
]
}
Otherwise you may need to have your Whatever class have the #JsonSetter annotation on a setEyes method with String parameter where you parse the String to manually create the list of Color yourself.
#JsonSetter
public void setEyes(final String eyes) {
// Parse string and set field here
}
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.