replace field name using #JsonTypeInfo - java

I would like to know if there is a way to replace the fieldname using #JsonTypeInfo
Here is what I want to achieve
class Tnode<T>{
#JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT, property="type")
T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
I get output as
{
"obj": {
"Foo": {
"name": "xyz"
}
}
}
The whole point is I do not want an extra layer "obj" as field name. I want the "Foo" to be one level above. In the code I am setting generic type to a concrete type. I want concrete class name to show up rather than having it wrapped.
I did try changing to include=As.PROPERTY but it will stil output as "obj".
I did solve using custom serializer. But I have to set every field.
public class CustomSerializer extends JsonSerializer<Object> {
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeObjectField("somename", value);
jgen.writeEndObject();
}
}
But the problem is when ever I have to add a instance variable in Tnode class I have to add that code in the custom serializer. And I want to avoid that.
Any suggestions?

There is no way to do that. Name of the property to contain Object, wrapped in type information, has to be statically known (to locate logical property). It can not vary.

Related

Get type of property in deserializer (Jackson)

I'm looking for a way to deserialize a subclass using a deserializer registered using the #JsonDeserialize annotation on the abstract super class. If there are better options I'm happy to adapt these options – but I'm not aware of any solution to this problem at the moment.
The core problem is: There is an abstract super class A:
#JsonSerialize(using = SerializerForA.class)
#JsonDeserialize(using = DeserializerForA.class)
public abstract class A {
private String value;
protected A(String value) {
this.value = value;
}
...
}
(The annotations are my attempt to do custom deserialization – maybe it's the wrong approach).
There are some derived classes, and A doesn't know any of the derived classes. Think about A is part of a framework and the derived classes are client code using the framework. Here are two derived classes:
public class B extends A {
public B(String value) {
super(value);
}
...
}
and
public class C extends A {
public C(String value) {
super(value);
}
...
}
These derived classes are used in a "container" class, e.g.:
public class MyClass {
private B b;
private C c;
...
}
And the corresponding JSON looks like this:
{
"b": "value_of_b",
"c": "value_of_c"
}
Writing a serializer is relatively simple:
public class SerializerForA extends JsonSerializer<A> {
#Override
public void serialize(A obj, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(obj.getValue());
}
}
The deserializer would look like this:
public class DeserializerForA extends JsonDeserializer<A> {
#Override
public A deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
A result = ???
return result;
}
}
But how does the deserializer know, which type the resulting object has? Is it possible to get the class name from one of the parameters (e.g. DeserializationContext)?
There are some ways the code can be changed, if it helps. For example, a setter can be used for the value field, instead of the constructor, but I would prefer a constructor, or some factory method (public static A getInstance(String value) { ... }).
Edit (1) The deserializer should be called without any specific code automatically by the ObjectMapper, like:
ObjectMapper mapper = new ObjectMapper();
MyClass myClass = mapper.readValue(json, MyClass.class);
That means, Jackson knows the type of the container class. It also knows the type of the properties a and b.
The reason to use a custom deserializer is that I need to have control over the instance creation process (basically, I want to reuse the same instance of each object for the same value – similar to an enum).
Edit (2) Changing the JSON structure is not an option. But I don't think it should be necessary. If I didn't need to have control over instance creation, the whole implementation would just work out of the box. Additional type information in the JSON should not be necessary.
Edit (3) The purpose of all of this is to implement a framework that can be used by application to create typesafe objects that are stored as JSON. Normally, I would use a Java enum for this purpose, but it is possible, that clients need to read JSON documents that are created by a new version of the framework (with new values), but the client didn't update the framework version yet.
Example:
There is a class called Currency:
public class Currency extends A {
public static final Currency EUR = new Currency("EUR");
}
It is used like this:
public class Transaction {
private Currency currency;
private double amount;
}
The JSON would look like this:
{
"currency": "EUR",
"amount": 24.34
}
Now a new currency is added:
public class Currency extends A {
public static final Currency EUR = new Currency("EUR");
public static final Currency USD = new Currency("USD");
}
Clients with the new framework can produce the following JSON:
{
"currency": "USD",
"amount": 48.93,
}
One client didn't update to the new framework version. This client should be able to read the JSON without crashing.
To sum up, the ObjectMapper is provided with an instance of MyClass containing one B and one C.
Jackson will call the JsonDeserializer<A> both for B and C providing the string "value_of_b" / "value_of_c" (because by reflection, it will know that B and C are instances of A and that's the only deserializer available in the context).
Considering that in the Jackson deserializer you are in a static context (you don't have any concrete instance of A in there, you're just deserializing some string text with information that allows you to create a new instance of MyClass that looks like the serialized instance that they provided you with), then I think the only option you have is to create a factory method somewhere in your code as you guessed (I'd create it directly in the A class):
public static A getInstance(String value) {
...
}
and then inside the deserializer, simply instantiate it from that independently on whether the serialized instance was a B or a C (cause at the end of the day, you only know A so you can't handle anything else):
public final class ADeserializer extends JsonDeserializer<A> {
#Override
public A deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
String value = jsonParser.getText();
return A.getInstance(value);
}
}
So basically each implementation will provide you with the String value that you need to create an A, and of course you will have to create a concrete basic implementation of A on your side in order to instantiate it (because you don't know what the other implementations are, and because you need it to be concrete to create an instance).
You have to include some information during the serialization in the json. There are two ways to achieve that.
First is to enable default typing. This will add class names to your json. It will look like this:
{
"a": [
"A",
{
"value": "a"
}
],
"b": [
"B",
{
"value": "b"
}
]
}
You can enable it on ObjectMapper by calling activateDefaultTyping(ptv, DefaultTyping.OBJECT_AND_NON_CONCRETE)
Second one is to add per-class annotations. You can achieve that by adding those annotations to your abstract class.
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#Type(value = A.class, name = "a"),
#Type(value = B.class, name = "b")
})
Then the serializer will produce json like this:
{
"a": {
"type": "a",
"value": "value_of_a"
}
"b": {
"type": "b",
"value": "value_of_b"
}
}
A simple solution – that even doesn't need a lot of magic – is to use a factory method and #JsonCreator:
The base class is already known, and also the serializer:
#JsonSerialize(using = SerializerForA.class)
public class A {
protected String value;
public String getValue() {
return value;
}
}
public class SerializerForA extends JsonSerializer<A> {
#Override
public void serialize(A a, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
gen.writeString(a.getValue());
}
}
The inherited classes need to implement a factory method each:
public class B extends A {
#JsonCreator
public static B create(String value) {
B b = new B();
b.value = value;
return b;
}
}
and
public class C extends A {
#JsonCreator
public static C create(String value) {
C c = new C();
c.value = value;
return c;
}
}
Now the following JSON is parsed successfully:
{
"b":"This is B",
"c":"This is C"
}
The obvious downside is, that inherited classes have to implement the factory method. I'd like to avoid that.

Custom annotation based ignore field from serialisation

Consider a case that I have 2 instance of Object Mapper.
I want one must exclude fields that are annotated with some custom annotation from serialization
While other mapper includes(ignores annotation)
Like class has 3 fields a,b,c and c is annotated with some annotation (say #IgnoreField)
(Their will n number of class, each will have their Fields that are not meant to be serialized)
Now 1st object mapper o1 must serialize only a and b.
While 2nd object mapper o2 can serialize a,b and c.
This can happen with any class having different fields some of which may be annotated.
You can always implement a custom JsonSerializer and register it with your ObjectMapper.
class Bean {
#Ignore
String a;
String b;
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#interface Ignore {
}
class BeanWithIgnoredFieldsSerializer extends JsonSerializer<Bean> {
#Override
public void serialize(final Bean value, final JsonGenerator gen, final SerializerProvider serializers) throws IOException, JsonProcessingException {
gen.writeStartObject();
try {
for (final Field f : Bean.class.getFields()) {
if (f.isAnnotationPresent(Ignore.class)) {
gen.writeStringField(f.getName(), (String) f.get(value));
}
}
} catch (final Exception e) {
//
}
gen.writeEndObject();
}
}
class BeanModule extends SimpleModule {
BeanModule() {
addSerializer(Bean.class, new BeanWithIgnoredFieldsSerializer());
}
}
void configure(final ObjectMapper om) {
om.registerModule(new BeanModule());
}
Note I have not tested this code, but that is the general idea how you add custom serializers to the OM. Adjust the code within the serialize method however you want.
Try configure SimpleBeanPropertyFilter for different condition.
#JsonFilter("someBeanFilter")
public class SomeBean {
}
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
filterProvider.addFilter("someBeanFilter",SimpleBeanPropertyFilter.serializeAllExcept("aFild"));
ObjectMapper mapper = new ObjectMapper();
mapper.setFilterProvider(filterProvider);
A distinct non-answer:
This is most likely a terrible idea.
You write code to communicate your intention. When you use that annotation, then you are telling "everybody" that these fields should be ignored.
A human reader looking at your code might spend half a day asking himself later "it says #IgnoreField for a and c , so why the heck are a, and c showing up serialized data?"
In other words, whatever problem you are trying to solve here, the answer is most likely not by hacking your way into ignoring annotations sometimes.
The next best "reasonable" solution might be: to rely on different custom annotations, like #IgnoreAlways and something like #OnlyIncludeForXyz. In other words: clearly express what might happen. Instead of using declarative programming, to then "lie" about what you declared.

How can I use Jackson serialization to write a field's value but not its key

Suppose I have a simple class:
public class MyClass {
private final String id;
private final String additionalJson;
}
I want to serialize this class to JSON so that the raw value in additionalJson is written directly into the JSON without writing the field's name.
Additionally, the id field should be transformed so that its name is written as identifier.
Example
The values of an instance are:
id = 1234
additionalJson = "someKey": "someValue"
The serialized JSON I want should be:
{
"identifier": "1234",
"someKey": "someValue"
}
Changing the name of id is trivial to do using Mixins:
public class MyClassMixin {
#JsonProperty("identifier")
private final String id;
}
But I don't have a good way to include the raw JSON without adding the field's name.
My current solution is to write a custom JsonSerializer for MyClass which writes the fields value using writeRaw():
public class MyClassSerializer extends JsonSerializer<MyClass> {
#Override
public void serialize(MyClass myClass, JsonGenerator json, SerializerProvider serializerProvider) throws IOException {
try {
json.writeStartObject();
for (Field field : myClass.getClass().getDeclaredFields()) {
if (Objects.equals(field.getName(), "additionalJson")) {
json.writeRaw(", " + (String)field.get(angebotsTemplate));
} else {
json.writeObjectField(field.getName(),
}
}
json.writeEndObject();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
This correctly includes the additionalJson value (after making the MyClass fields public) but doesn't make use of the Mixin. While I could add code to the serializer to transform the id name I would rather still do this in a Mixin as MyClass may have many fields, each with their own transformations I don't want to define in the Serializer.
Is there a way to achieve what I want, preferably relying on Mixins?

Jackson: serialize non-initialized collection field as empty

I have a POJO with a field or property, containing collection of objects, something like this:
public class Box {
public List<Items> items;
}
By default, value of items is null, and I do not want to initialize it with empty list.
Now, if I try to serialize it with Jackson, I get NullPointerException. Is there a simple way to make Jackson not break on such value and serialize it as an empty collection: [ ]?
Note. This class is just a simplified example. In reality, there are a hundred of classes and a number of fields with different names in each of them, which are occasionally set to null sometimes somewhere in the code, breaking serialization in runtime.
If you do not want to change the contract of your POJO class, think about the possibility to define custom Jackson serializer / deserializer which extend JsonSerializer<T> and JsonDeserializer<T> respectively. E.g.:
public class CountryDeserializer extends JsonDeserializer<CountryCode> {
#Override
public CountryCode deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException {
return CountryCode.getByCode(jp.getText());
}
}
and then
#JsonDeserialize(using=CountryDeserializer.class)
private CountryCode country;
You can check whether your field is null and act accordingly, in both directions (serialization / deserialization).
Have you considered making this class a JavaBean?
In that case, you would be able to give a default value in the getter:
public class Box {
private List<Items> items;
public List<Items> getItems() {
if(null == items) {
return Collections.emptyList();
}
return this.items;
}
//Setter here
}
This approach would prevent a lot of trouble related to Jackson's assumptions.
Update: Based on clarification... You could implement a custom serializer for the list type (and/or any other desired customization). Please note that :
public class ListSerializer extends JsonSerializer<List> {
#Override
public void serialize(List value, JsonGenerator jgen, SerializerProvider provider) throws IOException,
JsonProcessingException {
if (null == value) {
provider.defaultSerializeValue(new ArrayList<Object>(), jgen);
} else {
provider.defaultSerializeValue(value, jgen);
}
}
}
//Then your code could set the serializer on the object mapper as follows:
objectMapper.addSerializer(List.class, new ListSerializer());
Repeat for all such customization.
Code was inspired by this article: http://www.baeldung.com/jackson-custom-serialization

Jackson How to retrieve parent bean in a custom Serializer/Deserializer

In a custom serializer/deserializer, is there a way to retrieve the parent bean of the field?
For example:
public class Foo {
#JsonSerialize(using = MyCustomSerializer.class)
public Bar bar;
}
public class Bar { }
public class MyCustomSerializer extends JsonSerializer<Bar> {
#Override
public void serialize(
Bar value,
JsonGenerator jgen,
SerializerProvider serializers)
throws IOException, JsonProcessingException
{
// get Foo ??
}
}
Here I'd like to get Foo in my serializer without having to have a reference inside Bar.
If you are using Jackson 2.5, it is possible to access parent object via JsonGenerator.getCurrentValue(). Or, further up the hierarchy, going via getOutputContext() (which has getParent() as well as getCurrentValue() method).
This is also available through JsonParser for custom deserializer.
For deserialization, where you don't have access to the JsonGenerator object. The following worked for me:
JsonStreamContext parsingContext = jsonParser.getParsingContext();
JsonStreamContext parent = parsingContext.getParent();
Object currentValue = parent.getCurrentValue();
Note: getCurrentValue will be null if you are using custom serialization
I worked around this by setting the parent object into the child's serializer instance and then accessing it when the child's serializer was called by jackson.

Categories