Override #JsonInclude(Include.NON_NULL) from POJO in ObjectMapper - java

Question: Is it possible to override Include.NON_NULL defined in the POJO while creating the ObjectMapper?
Explanation:
Suppose I have a POJO as below:
#JsonInclude(Include.NON_NULL)
class POJO {
String name;
String description;
//Constructors, Getters & Setters, etc
}
And a test class as below:
class Main {
public static void main(String args[]) {
POJO p = new POJO();
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.setSerializationInclusion(Include.ALWAYS)
.writerWithDefaultPrettyPrinter()
.writeValueAsString(p);
//jsonString should contain empty name & description fields, but they doesn't
}
}

You can use a mix-in, since it has priority over annotations.
#JsonInclude(JsonInclude.Include.ALWAYS)
public class MixIn {
}
And add it to the mapper
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(POJO.class, MixIn.class);
The result will be
{"name":null,"description":null}

Related

Read part of yaml in Java using Jackson

if I have the following yaml (which I found online) representing a java Order class, order.yaml:
orderNo: A001
customerName: Customer, Joe
orderLines:
- item: No. 9 Sprockets
quantity: 12
unitPrice: 1.23
- item: Widget (10mm)
quantity: 4
unitPrice: 3.45
I was able to use
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
Order order = objectMapper.readValue(new File(<path_to_order>), Order.class);
But this means that I need to define orderNo and orderLines in advance... If I have a giant yaml with a bunch of nested properties this can get really annoying. What if I want a class than can read one property or a class that can read another property and "ignore" other ones? Is that even possible? That way I could just specify which java object I want without necessarily having to recursively define every property of the yaml. Thank you!
The Map approach will lose you the type safety. There's no need to define every single property. You can use the Json annotations just fine with YAML too, it's just a historical leftover that it is called Json. What you are looking for is #JsonIgnoreProperties(ignoreUnknown = true).
If you don't like to specify the Annotation for every class, use objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);.
public static void main(String[] args) throws IOException {
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
Order order = objectMapper.readValue(new File("foo.yml"), Order.class);
System.out.println(order.getOrderLines().get(0).getItem());
}
#JsonIgnoreProperties(ignoreUnknown = true)
static class Order {
private String orderNo;
private List<OrderLine> orderLines;
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public List<OrderLine> getOrderLines() {
return orderLines;
}
public void setOrderLines(List<OrderLine> orderLines) {
this.orderLines = orderLines;
}
#JsonIgnoreProperties(ignoreUnknown = true)
static class OrderLine {
private String item;
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
}
}
You can read the json in a Map and then retrieve whatever you want from there
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
Map<String,Object> jsonMap = objectMapper.readValue(new File(<path_to_order>), Map.class);

Is it possible to deserialize values missing from JSON to some default values?

As an example class:
#Getter #Setter
public static class SomeClass {
private String notNull;
private String nullSetEmpty;
private String notExists;
}
Deserialization of null values to empty is possible by overriding configuration, like:
String json = " {\"notNull\": \"a value\", \"nullSetEmpty\": null}";
ObjectMapper om = new ObjectMapper();
om.configOverride(String.class)
.setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
SomeClass sc = om.readValue(json, SomeClass.class);
System.out.print(om.writerWithDefaultPrettyPrinter().writeValueAsString(sc));
This produces:
{
"notNull" : "a value",
"nullSetEmpty" : "",
"notExists" : null
}
But how about this notExists. It is possible to add default value to each class having the problem but is there any generic way to do that like configOverride does so that Jackson handles that?
you can just define default value in your data class
#Getter
#Setter
public static class SomeClass {
private String notNull;
private String nullSetEmpty;
private String notExists = "default-value";
}

How to convert embedded JSON String inside JSON to java object?

I have the below json, where the body key contains a value which is a string representation of a JSON object, how do I convert it to a Java Object ?
I can extract the body value by converting the JSON to a Map, but I don't know how I should proceed from there
input.json file
{
"body": "{\n\t\"username\": \"TestUser\",\n\t\"password\": \"TestPassword\"\n}"
}
The User POJO is as below,
class User {
private String username;
private String password;
... getters, setters and no-arg constructor
}
My code looks something like this, I need to implement convertToUser function
public static void main(String[] args) {
String jsonContent = readJsonFile("input.json");
String escapedJsonBody = getBody(s);
User user = convertToUser(escapedJsonBody, User.class);
}
I am already using jackson java library, any insights on doing this with jackson is highly appreciated.
One way to do it is to create DTOs and converter. Having DTOs like (i have nested the class declarations jsut to save space in answer):
#Getter #Setter
public class Input { // this level maps to the whole input.json
#JsonDeserialize(using = BodyDeserializer.class) // custom deserializer below
private Body body; // this is the body-attribute in JSON
#Getter #Setter
public static class Body {
private User user;
#Getter #Setter
public static class User {
private String username;
private String password;
}
}
}
the converter:
public class BodyDeserializer extends JsonDeserializer<Body> {
private ObjectMapper om = new ObjectMapper(); // this is to read the user from string
#Override
public Body deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String embedded = p.readValueAs(String.class);
Body body = new Body();
body.setUser(om.readValue(embedded, User.class)); // here is the trick
return body;
}
}
Use like:
ObjectMapper om = new ObjectMapper();
String input = "{\"body\": \"{\\n\\t\\\"username\\\": \\\"TestUser\\\",\\n\\t\\\"password\\\": \\\"TestPassword\\\"\\n}\"}";
Input r = om.readValue(input, Input.class);
This way the conversion happens in generic way only con might be that you do not like to create DTOs and dig the user like Input.getBody().getUser();
To convert a JSON String to a java pojo you can use Jackson's ObjectMapper class that will assist you to do this.
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.readValue(inputJson, User.class);
More info can be found on Jackson's github page

Deserialize nested array as ArrayList with Jackson

I have a piece of JSON, that looks like this:
{
"authors": {
"author": [
{
"given-name": "Adrienne H.",
"surname": "Kovacs"
},
{
"given-name": "Philip",
"surname": "Moons"
}
]
}
}
I have created a class to store Author information:
public class Author {
#JsonProperty("given-name")
public String givenName;
public String surname;
}
And two wrapper classes:
public class Authors {
public List<Author> author;
}
public class Response {
public Authors authors;
}
This is working, but having two wrapper classes seems to be unnecessary. I want to find a way to remove Authors class and have a list as a property of Entry class. Is something like that is possible with Jackson?
Update
Solved that with custom deserializer:
public class AuthorArrayDeserializer extends JsonDeserializer<List<Author>> {
private static final String AUTHOR = "author";
private static final ObjectMapper mapper = new ObjectMapper();
private static final CollectionType collectionType =
TypeFactory
.defaultInstance()
.constructCollectionType(List.class, Author.class);
#Override
public List<Author> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException {
ObjectNode objectNode = mapper.readTree(jsonParser);
JsonNode nodeAuthors = objectNode.get(AUTHOR);
if (null == nodeAuthors // if no author node could be found
|| !nodeAuthors.isArray() // or author node is not an array
|| !nodeAuthors.elements().hasNext()) // or author node doesn't contain any authors
return null;
return mapper.reader(collectionType).readValue(nodeAuthors);
}
}
And using it like this:
#JsonDeserialize(using = AuthorArrayDeserializer.class)
public void setAuthors(List<Author> authors) {
this.authors = authors;
}
Thanks #wassgren for the idea.
I see at least two approaches to do this if you want to get rid of wrapper classes. The first is to use the Jackson Tree Model (JsonNode) and the second is to use a deserialization feature called UNWRAP_ROOT_VALUE.
Alternative 1: Use JsonNode
When deserializing JSON using Jackson there are multiple ways to control what type of objects that are to be created. The ObjectMapper can deserialize the JSON to e.g. a Map, JsonNode (via the readTree-method) or a POJO.
If you combine the readTree-method with the POJO conversion the wrappers can be completely removed. Example:
// The author class (a bit cleaned up)
public class Author {
private final String givenName;
private final String surname;
#JsonCreator
public Author(
#JsonProperty("given-name") final String givenName,
#JsonProperty("surname") final String surname) {
this.givenName = givenName;
this.surname = surname;
}
public String getGivenName() {
return givenName;
}
public String getSurname() {
return surname;
}
}
The deserialization can then look something like this:
// The JSON
final String json = "{\"authors\":{\"author\":[{\"given-name\":\"AdrienneH.\",\"surname\":\"Kovacs\"},{\"given-name\":\"Philip\",\"surname\":\"Moons\"}]}}";
ObjectMapper mapper = new ObjectMapper();
// Read the response as a tree model
final JsonNode response = mapper.readTree(json).path("authors").path("author");
// Create the collection type (since it is a collection of Authors)
final CollectionType collectionType =
TypeFactory
.defaultInstance()
.constructCollectionType(List.class, Author.class);
// Convert the tree model to the collection (of Author-objects)
List<Author> authors = mapper.reader(collectionType).readValue(response);
// Now the authors-list is ready to use...
If you use this Tree Model-approach the wrapper classes can be completely removed.
Alternative 2: remove one of the wrappers and unwrap the root value
The second approach is to remove only one of the wrappers. Assume that you remove the Authors class but keep the Response-wrapper. If you add the a #JsonRootName-annotation you can later unwrap the top-level name.
#JsonRootName("authors") // This is new compared to your example
public class Response {
private final List<Author> authors;
#JsonCreator
public Response(#JsonProperty("author") final List<Author> authors) {
this.authors = authors;
}
#JsonProperty("author")
public List<Author> getAuthors() {
return authors;
}
}
Then, for your mapper simply use:
ObjectMapper mapper = new ObjectMapper();
// Unwrap the root value i.e. the "authors"
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
final Response responsePojo = mapper.readValue(json, Response.class);
The second approach only removes one of the wrapper classes but instead the parsing function is quite pretty.

Using #JsonIdentityInfo without annotations

I use Jackson 2.2.3 to serialize POJOs to JSON. Then I had the problem, that I couldn't serialize recursive structures...I solved this problem by using #JsonIdentityInfo => works great.
But, I don't want this annotation on the top of my POJO.
So my question is: Is there any other possibility to set the default behavior of my ObjectMapper to use the feature for every POJO?
So I want to transform this annotation code
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#id")
to something like
ObjectMapper om = new ObjectMapper();
om.setDefaultIdentityInfo(ObjectIdGenerators.IntSequenceGenerator.class, "#id");
Any ideas?
You can achieve that using the Jackson mix-in annotations or the Jackson annotation introspector.
Here is an example showing both methods:
public class JacksonJsonIdentityInfo {
#JsonIdentityInfo(
generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "#id")
static class Bean {
public final String field;
public Bean(final String field) {this.field = field;}
}
static class Bean2 {
public final String field2;
public Bean2(final String field2) {this.field2 = field2;}
}
#JsonIdentityInfo(
generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "#id2")
static interface Bean2MixIn {
}
static class Bean3 {
public final String field3;
public Bean3(final String field3) {this.field3 = field3;}
}
static class MyJacksonAnnotationIntrospector extends JacksonAnnotationIntrospector {
#Override
public ObjectIdInfo findObjectIdInfo(final Annotated ann) {
if (ann.getRawType() == Bean3.class) {
return new ObjectIdInfo(
PropertyName.construct("#id3", null),
null,
ObjectIdGenerators.IntSequenceGenerator.class,
null);
}
return super.findObjectIdInfo(ann);
}
}
public static void main(String[] args) throws JsonProcessingException {
final Bean bean = new Bean("value");
final Bean2 bean2 = new Bean2("value2");
final Bean3 bean3 = new Bean3("value3");
final ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(Bean2.class, Bean2MixIn.class);
mapper.setAnnotationIntrospector(new MyJacksonAnnotationIntrospector());
System.out.println(mapper.writeValueAsString(bean));
System.out.println(mapper.writeValueAsString(bean2));
System.out.println(mapper.writeValueAsString(bean3));
}
}
Output:
{"#id":1,"field":"value"}
{"#id2":1,"field2":"value2"}
{"#id3":1,"field3":"value3"}
After several months and a lot of research, I've implemented my own solution to keep my domain clear of jackson dependencies.
public class Parent {
private Child child;
public Child getChild(){return child;}
public void setChild(Child child){this.child=child;}
}
public class Child {
private Parent parent;
public Child getParent(){return parent;}
public void setParent(Parent parent){this.parent=parent;}
}
First, you have to declare each of your entities of the bidirectional relationship:
public interface BidirectionalDefinition {
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id", scope=Parent.class)
public interface ParentDef{};
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id", scope=Child.class)
public interface ChildDef{};
}
After that, the object mapper can be automatically configured:
ObjectMapper om = new ObjectMapper();
Class<?>[] definitions = BidirectionalDefinition.class.getDeclaredClasses();
for (Class<?> definition : definitions) {
om.addMixInAnnotations(definition.getAnnotation(JsonIdentityInfo.class).scope(), definition);
}

Categories