How to ignore json property in encapsulated design - java

class A{
private B b;
//other properties
//getter setter
}
// unable to add jsonIgnore in this class due to dependency in other module
class B {
int id;
String name;
String defname;
}
I want to ignore defname in class A JSON building by codehaus.jackson API.
I need {a:{id:value,name:value}}.

You can use Mixin for this purpose.
First Create an abstract class with JsonIgnore annotation:
abstract class MixIn{
#JsonIgnore
abstract String getDefname(); }
Then use it as below. (Be sure your getter name of defName field as getDefName() in your B class or change it in Mixin class as yours.)
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn( B.class, MixIn.class );
objectMapper.writeValue( System.out, new A() );
This prints:
{"b":{"id":1,"name":"Sercan"}}

Related

How to declare a variable or Object of any class type in Java

I am quite new to Java and I am trying to deserialize the JSON using Jackson and I facing some minor issue with regards to declaring the Object/Variable type. I will provide all the codes then explain the issue for easy understanding.
I have an enum that will have the required type values:
public enum IdentifierTypeValues {
Type1,
Type2,
Type3,
//Constructor and Getter of enum values
}
Then for each of these type, I have different classes which will have different input and do a completely different type of process:
public class GenerateType1 {
private String name;
private String age;
//Getter and Setter
//Some required process based on these values
}
public class GenerateType2 {
private String address;
private String city;
private String country;
//Getter and Setter
//Some required process based on these values
}
public class GenerateType3 {
private String firstName;
private String lastName;
private String fullName;
//Getter and Setter
//Some required process based on these values
}
Now I have a wrapper class for these type of classes which will take the type based on enum and typeInfo values. I want the typeInfo values to be any of the class based type something like this:
public class TypeSyntax {
private IdentifierTypeValues indeitiferType;
private GenerateType1 / GenerateType2 / GenerateType3 identifierTypeValues;
//Here the identifierTypeValues can have the values for anytype
//How to declare a variable of any of these class type?
}
This is the class that will be used by my JSON for deserializing. I know I can add a wrapper class of those 3 types and provide that wrapper class as a type class for this. Something like this:
public class WrapperClass{
private GenerateType1 type1;
private GenerateType2 type2;
private GenerateType3 type3;
}
public class TypeSyntax{
private IdentifierTypeValues indeitiferType;
private WrapperClass identifierTypeValues;
//But using this approach will change my JSON structure which I do not want to do.
}
My JSON structure is something like this and I would like to keep it in the same way.
{
"indeitiferType":"Type1",
"identifierTypeValues":{
"name":"Batman",
"age":"2008"
}
}
Is there a way I can declare the variable of multiple type class? or any better approach to handle this by keeping the json format same? I tried searching but I am unable to search what exactly so any help would be really appriciated.
Because the type identifier exists on a different level than the other properties a wrapper class TypeSyntax needed. There are several open feature requests to add wrapping functionality to Jackson e.g. https://github.com/FasterXML/jackson-databind/issues/512
Fortunately polymorphism is supported in Jackson with #JsonTypeInfo and #JsonSubTypes annotations.
Wrapper class should look like:
public class TypeSyntax {
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
property = "identifierType")
private GenerateTypeBase identifierTypeValues;
// getters and setters (omitted for brevity)
}
GenerateTypeBase is the common parent class
#JsonSubTypes({
#JsonSubTypes.Type(value = GenerateType1.class, name = "Type1"),
#JsonSubTypes.Type(value = GenerateType2.class, name = "Type2"),
})
public abstract class GenerateTypeBase {
private String name;
private String age;
// getters and setters (omitted for brevity)
}
In this different children classes will instantiated based on the identifierType property.
The children must extend this base class:
public class GenerateType2 extends GenerateTypeBase {
// additional properties
}
In a short test it will be:
#Test
void wrapperTest() throws IOException {
ObjectMapper mapper = new ObjectMapper();
GenerateType2 a = new GenerateType2();
a.setName("Foo");
a.setAge("13");
TypeSyntax w = new TypeSyntax();
w.setIdentifierTypeValues(a);
String json = mapper.writeValueAsString(w);
System.out.println(json);
}
and the output:
{
"identifierTypeValues":
{
"name":"Foo",
"age":"13"
},
"identifierType":"Type2"
}
Deserialization
#Test
void wrapperTest() throws IOException {
ObjectMapper mapper = new ObjectMapper();
String input = "{\"identifierTypeValues\": \"name\":\"Foo\",\"age\":\"13\"},\"identifierType\":\"Type2\"}";
TypeSyntax w = mapper.readValue(new StringReader(input), TypeSyntax.class);
assertAll(
() -> assertEquals(GenerateType2.class, o.getIdentifierTypeValues().getClass()),
() -> assertEquals("13", o.getIdentifierTypeValues().getAge())
);
}
If you want more flexibility you can write custom (de)serializer and / or custom resolver. Using custom TypeIdResolver that will possible to convert identifiers to types programmatically instead of using "key-value pairs" in #JsonSubTypes

Jackson json only convert selected fields and methods

With jackson there is a way to ignore some fields using #JsonIgnore. Is there a way to do the opposite, and only show fields with are annotated? I'm working with an external class with a lot of fields and I only want to select a small subset of them. I'm getting tons of recursion problems (using some type of ORM) where object A -> B -> A -> B -> A .... which are not even necessary to export.
You can configure the object mapper to ignore absolutely everything unless specified by JsonProperty,
public class JacksonConfig {
public static ObjectMapper getObjectMapper(){
//The marshaller
ObjectMapper marshaller = new ObjectMapper();
//Make it ignore all fields unless we specify them
marshaller.setVisibility(
new VisibilityChecker.Std(
JsonAutoDetect.Visibility.NONE,
JsonAutoDetect.Visibility.NONE,
JsonAutoDetect.Visibility.NONE,
JsonAutoDetect.Visibility.NONE,
JsonAutoDetect.Visibility.NONE
)
);
//Allow empty objects
marshaller.configure( SerializationFeature.FAIL_ON_EMPTY_BEANS, false );
return marshaller;
}
}
public class MyObject {
private int id;
#JsonProperty
private String name;
private Date date;
//Getters Setters omitted
in this case only name would be serialized.
Sample repo, https://github.com/DarrenForsythe/jackson-ignore-everything
Yes definitely you can; Create a class with only the feilds you need and add the below property in the object mapper and rest is done.
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES to false
You can use #JsonIgnoreProperties(ignoreUnknown=true) on the pojo class so only the fields which are available in the pojo class will be mapped and resf will be left out.
For example
Json data
{
"name":"Abhishek",
"age":30,
"city":"Banglore",
"state":"Karnatak"
}
pojo class
#JsonIgnoreProperties(ignoreUnknown=true)
Class Person{
private int id;
private String name;
private String city;
}
Here state in not present in the Person class so that field won't be mapped

Ignore #JsonValue annotation without subclassing

Let's suppose there is a class hierarchy in some third-party library. One of those classes has #JsonValue annotation. It results in serialization/deserialization of only one field. Is it possible to ignore #JsonValue annotation without subclassing and get all object fields serialized?
You can define a MixIn and override the #JsonValue via property or getter
- depending on where the annotation has been set in the third-party library.
Assuming we have a class Pojo with a #JsonValue annotated member prop:
class Pojo {
#JsonValue
private String prop;
private String prop2;
// getters and setters
}
We then define an abstract MixIn class like follows, overriding the particular member or method:
abstract class MixIn {
#JsonValue(false) // either annotating members
private String prop;
#JsonValue(false) // or annotating methods
abstract String getProp();
}
This can be added to the ObjectMapper like follows:
objectMapper.addMixIn(Pojo.class, MixIn.class);
Instead of just prop, now both prop and prop2 are serialized.

Jackson's #JsonPropertyOrder doesn't work with #JsonUnwrapped

I have an object that contains another object attribute like this:
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
#JsonPropertyOrder({"fA1","b","fA2"})
#Data
public class A {
private String fA1;
private String fA2;
#JsonUnwrapped
private B b = new B();
#Data
class B {
private String fB1;
private String fB2;
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
A a = new A ();
System.out.println(objectMapper.writeValueAsString(a));
}
}
what i want is generate json that respect this order :
{
"fA1":"",
"fB1":"",
"fA2":"",
"fB2":""
}
Is there any way to do this?
According to this issue in the jackson-databind repository on GitHub, the #JsonPropertyOrder annotation doesn't work with #JsonUnwrapped annotation. See the quote below:
True, unwrapped properties are not included in ordering, since they are not really known by enclosing serializer as regular properties. And specifically, as things are, will be output as a block of properties per contained, so even if known they can not be reordered separately.
Perhaps something could be done with Jackson 3.x once we get there.
But you may consider a workaround: as you seem to be using Lombok, you could annotate b with #Delegate and #JsonIgnore:
#Data
#JsonPropertyOrder({"fa1", "fb1", "fa2", "fb2"})
public class A {
private String fA1;
private String fA2;
#Delegate
#JsonIgnore
private B b = new B();
}
The #JsonIgnore annotation will ensure that the b property is not serialized by Jackson.
And the #Delegate annotation will tell Lombok to generate delegate methods that forward the call to the B methods. With that, the A class will have getters and setters that are delegated to the getters and setters of the fB1 and fB2 fields.

How to add custom deserializer to interface using jackson

Saying I have an interface A, I want to use custom deserializer for all classes implement interface A, So I use code below but it doesn't work, While CustomAserializer works.
So what should I do to deserialize all classes implement A using my custom deserializer.
Thanks.
module.addDeserializer(A.class, new CustomADeserializer());
module.addSerializer(A.class, new CustomASerializer())
It seems you forgot to annotate your implementation classes with #JsonDeserialize(using = ImplementationClazz.class) to indicate that the class should be used to deserialize the abstract class or interface.
The following is a simple example to deserialize an interface having multiple implementations using Jackson.
Here is my interface:
#JsonDeserialize(using = UserDeserializer.class)
public interface User {
}
One implementation of the interface:
#JsonDeserialize(as = ServiceUser.class)
public class ServiceUser implements User{
private String name;
private String role;
//constructor, setters & getters
Second implementation:
#JsonDeserialize(as = AdminUser.class)
public class AdminUser implements User {
private String role;
private String designation;
//constructor, setters & getters
And here is the deserializer:
public class UserDeserializer extends JsonDeserializer<User> {
#Override
public User deserialize(JsonParser jp, DeserializationContext context) throws IOException {
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
ObjectNode root = mapper.readTree(jp);
/*write your own condition*/
if (root.has("name") && root.get("name").asText().equals("XYZ")) {
return mapper.readValue(root.toString(), ServiceUser.class);
}
return mapper.readValue(root.toString(), AdminUser.class);
}
}
You may get a StackOverflowError if you don't annotate the implementation classes. All implementation classes should deserialize themselves, otherwise it will use the deserializer from the parent class which leads to a StackOverflowError.
Just in case someone need a solution to serialize and desiralize inheritance hierarchy
you can use jackson annotation in more elegant way : JsonTypeInfo and JsonSubTypes
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#Type(value = ServiceUser.class, name = "service"),
#Type(value = AdminUser.class, name = "admin")
})
public interface User{
// ...
}

Categories

Resources