Jackson #JsonValue annotation selectively override or disable - java

I have an enum with the following attributes:
private Integer id;
private String name;
private String description;
This enum has a function with Jackson's #JsonValue annotation:
#JsonValue
public String toValue() {
return Stream.of(values())
.filter(eventType -> eventType == this)
.findAny()
.map(EventType::toString)
.orElseThrow(() -> new IllegalArgumentException("Unable to convert EventType to value:" + this));
}
This performs as desired, serializing the enum values to just the value returned by toString, which is the name of the enum.
I want to be able to disable the #JsonValue annotation and just use Jackson's otherwise default JSON serialization behavior attached to this class: #JsonFormat(shape = JsonFormat.Shape.OBJECT) in certain cases where the object representing the entire enum must be fetched, instead of just the name.
Jackson does not appear to have this ability built in (either that or I do not understand it well enough). I cannot create a wrapper class that extends this class, as Java does not allow that with enums.
Any suggestions?

This can be achieved using a Mixin.
Keep your enum class the same, create a mixin class:
// MyEnumMixin.java
import com.fasterxml.jackson.annotation.JsonValue;
public abstract class MyEnumMixin {
#JsonValue(false)
public abstract String toValue();
}
Then add the mixin when you create your Mapper
// MyService.java
...
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(MyEnum.class, MyEnumMixin.class);
return mapper.writeValueAsString(pojoWithEnum);
...
Credit to https://stackoverflow.com/users/6911095/ldz for helping me solve something similar and being able to add an answer here. https://stackoverflow.com/a/59459177/7589862

I don't think of a ready made solution for this, but the closest option I can think of is to use a custom serializer for this specific enum type.
Adding a custom serializer is easy and just requires one to extend JsonSerializer class and implement serialize method, then use the class in the #JsonSerialize annotation to the type where needed (and you don't need #JsonValue). Enum object would be passed into the serialize method and there you can choose to write either toString or toValue.
An example code is provided below along with the output.
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.IOException;
import java.util.stream.Stream;
public class SOQuestion {
static ThreadLocal<Boolean> USE_TO_STRING = ThreadLocal.withInitial(() -> false);
#JsonSerialize(using = MySerializer.class)
enum EventType{
ONE(1, "One", "Just One"), TWO(2, "Two", "Just Two");
private Integer id;
private String name;
private String description;
EventType(Integer id, String name, String description) {
this.id = id;
this.name = name;
this.description = description;
}
public String toValue() {
return Stream.of(values())
.filter(eventType -> eventType == this)
.findAny()
.map(EventType::toString)
.map(s -> "toValue="+s)
.orElseThrow(() -> new IllegalArgumentException("Unable to convert EventType to value:" + this));
}
}
#Data
#AllArgsConstructor
static class Dummy{
int someNum;
EventType eventType;
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
Dummy dummy = new Dummy(100, EventType.ONE);
System.out.println("**** DEFAULT *****");
System.out.println(objectMapper.writeValueAsString(dummy));
System.out.println("**** toString *****");
USE_TO_STRING.set(true);
System.out.println(objectMapper.writeValueAsString(dummy));
}
private static class MySerializer extends JsonSerializer<EventType> {
#Override
public void serialize(EventType value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
if(USE_TO_STRING.get()){
gen.writeString(value.toString());
}else{
gen.writeString(value.toValue());
}
}
}
}
Output:
**** DEFAULT *****
{"someNum":100,"eventType":"toValue=ONE"}
**** toString *****
{"someNum":100,"eventType":"ONE"}
Apologies for using lombok which makes things simple. If you're not familiar, its time to pick it up.

Related

Jackson use the first non-null alias

I'm using Jackson to parse JSON data on a Spring Boot application, but Jackson is unable to select properly the JSON fields to map to the POJO fields.
Details
Firstly, I'm reading data from a third party library, and their data model has a lot of redundancy (namely the several properties can represent the same information), and I can't ask them to fix it.
Their JSON is something like that:
{ "name" : "name", "full_name" : "name", "fullName" : "name"}
there are 3 properties in JSON containing the same information. But sometimes there would be only one of these properties which is non-null, like:
{ "name" : null, "full_name" : "", "fullName" : "name"}
And that happens to many other properties.
I've tried to use #JsonAlias to extract the required (non-null) data from the incoming JSON, but it doesn't resolve the issue.
#JsonProperty("name")
#JsonAlias({"full_name","fullName"})
private String name;
First, that #JsonAlias is taking precedence from the #JsonProperty value. Second, it's not ignoring null values.
How can I make Jackson ignore null values in a situation like described above?
Multiple Setters
One of the possible options is to define a bunch of additional setters for each duplicated property, annotated with #JsonSetter to map each setter to a corresponding property flavor.
To avoid logic duplication, every additional setter should delegate to a regular setter, which should determine if the existing value needs to be updated (if it's empty, null, etc.).
public class MyPojo {
public static final Predicate<String> NULL_OR_EMPTY =
s -> s == null || s.isEmpty(); // predicate can be reused to check multiple properties
private String name;
#JsonSetter("name")
public void setName(String name) {
if (NULL_OR_EMPTY.test(this.name)) this.name = name;
}
#JsonSetter("full_name")
public void setName1(String name) {
setName(name);
}
#JsonSetter("fullName")
public void setName2(String name) {
setName(name);
}
}
Usage example:
String json = "{ "name" : null, "full_name" : "", "fullName" : "name"}";
ObjectMapper mapper = new ObjectMapper();
MyPojo myPojo = mapper.readValue(json, MyPojo.class);
System.out.println(myPojo);
Output:
MyPojo{name='name'}
The drawbacks of this approach are the usage of "smart-setters", which might not considered to be clean (because it violates the Single responsibility principle, setters aren't meant to perform validation) and domain class gets polluted with additional setter methods. Both issues can be solved by externalizing this functionality.
Custom Converter
Another possible solution is to customize deserialization by defining a Converter for the target type.
Note: don't confuse Converter and Deserializer. Deserializer is meant to provide logic how to construct a POJO based on the information contained in the JsonParser, whilst Converter is used to transform one POJO (which is easier to deserialize) into another POJO.
So we need to create two classes: Converter and auxiliary type reflecting the data model of the incoming JSON.
Consider the following auxiliary POJO:
public record AuxiliaryPojo(
#JsonProperty("name") String name,
#JsonProperty("full_name") String name1,
#JsonProperty("fullName") String name2
) {}
And that's the Converter extending StdConverter that bridges AuxiliaryPojo and MyPojo:
public class AuxiliaryPojoToMyPojo extends StdConverter<AuxiliaryPojo, MyPojo> {
public static final Predicate<String> NOT_NULL_OR_EMPTY =
s -> s != null && !s.isEmpty();
#Override
public MyPojo convert(AuxiliaryPojo v) {
return MyPojo.builder()
.name(findMatching(v.name(), v.name1(), v.name2()))
.build();
}
private String findMatching(String... args) {
return Arrays.stream(args)
.filter(NOT_NULL_OR_EMPTY)
.findFirst().orElse(null);
}
}
And here's the domain class (free from any redundant code). Note that Converter has been specified via converter property of the #JsonDeserialize annotation.
#Getter
#Builder
#JsonDeserialize(converter = AuxiliaryPojoToMyPojo.class)
public static class MyPojo {
private String name;
}
That would be enough to parse the sample JSON into an instance of MyPojo:
String json = "{ "name" : null, "full_name" : "", "fullName" : "name"}";
ObjectMapper mapper = new ObjectMapper();
MyPojo myPojo = mapper.readValue(json,bMyPojo.class);
System.out.println(myPojo);
Output:
MyPojo{name='name'}
An other solution is to ignore additionnal fields 'fullName' and 'full_name' using annotation #JsonIgnoreProperties and a custom deserializer that throws an exception when name property is null (or equal to string "null") so that you can catch that exception in order not to create a person when name is null.
See code below:
Class Person
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
#JsonIgnoreProperties({ "fullName", "full_name" })
public class Person {
#JsonProperty("name")
#JsonDeserialize(using = CustomNameDeserializer.class)
private String name;
public Person() {
super();
}
public String toString() {
return "name: " + name;
}
public static void main(String[] args) {
String[] personArrayJson = new String[3];
personArrayJson[0]="{ \"name\" : \"nameNotNull1\", \"full_name\" : \"name\", \"fullName\" : \"name\"}";
personArrayJson[1]="{ \"name\" : \"null\", \"full_name\" : \"\", \"fullName\" : \"name\"}";
personArrayJson[2]="{ \"name\" : \"nameNotNull2\", \"full_name\" : \"name\", \"fullName\" : \"name\"}";
List<Person> persons = new ArrayList<Person>();
ObjectMapper mapper = new ObjectMapper();
Person p;
for (String personJson : personArrayJson) {
try {
p = mapper.readValue(personJson, Person.class);
persons.add(p);
} catch (JsonProcessingException e) {
//e.printStackTrace();
System.out.println("NAme is null: not deserialized");
}
}
System.out.println("Persons list contains "+persons.size()+" persons => "+persons);
}
}
Class CustomNameDeserializer
import java.io.IOException;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
public class CustomNameDeserializer extends StdDeserializer<String> {
public CustomNameDeserializer(Class<String> s) {
super(s);
}
public CustomNameDeserializer() {
this(null);
}
#Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException, NullPointerException{
String n = p.getValueAsString();
if(n==null || n.equals("null") || n.trim().equals(""))
throw new NullPointerException("Name is null");
return n;
}
}
Hope it helps

Flat JSON in Jackson for class/record/value object with one field

I have a Java record with one field only:
public record AggregateId(UUID id) {}
And a class with the AggregateId field (other fields removed for readability)
public class Aggregate {
public final AggregateId aggregateId;
#JsonCreator
public Aggregate(
#JsonProperty("aggregateId") AggregateId aggregateId
) {
this.aggregateId = aggregateId;
}
}
The implementation above serialize and deserialize JSON with given example:
ObjectMapper objectMapper = new ObjectMapper();
String content = """
{
"aggregateId": {
"id": "3f61aede-83dd-4049-a6ff-337887b6b807"
}
}
""";
Aggregate aggregate = objectMapper.readValue(content, Aggregate.class);
System.out.println(objectMapper.writeValueAsString(aggregate));
How could I change Jackson config to replace JSON by that:
{
"aggregateId": "3f61aede-83dd-4049-a6ff-337887b6b807"
}
without giving up a separate class for AggregateId and access through fields, without getters?
I tried #JsonUnwrapper annotation, but this caused throws
Exception in thread "X" com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
Invalid type definition for type `X`:
Cannot define Creator parameter as `#JsonUnwrapped`: combination not yet supported at [Source: (String)"{
"aggregateId": "3f61aede-83dd-4049-a6ff-337887b6b807"
}"
or
Exception in thread "X" com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
Cannot define Creator property "aggregateId" as `#JsonUnwrapped`:
combination not yet supported at [Source: (String)"{
"aggregateId": "3f61aede-83dd-4049-a6ff-337887b6b807"
}"
Jackson version: 2.13.1
dependencies {
compile "com.fasterxml.jackson.core:jackson-annotations:2.13.1"
compile "com.fasterxml.jackson.core:jackson-databind:2.13.1"
}
Of course, it's possible with a custom serializer/deserializer, but I'm looking for an easier solution because I have many different classes with a similar issue.
The combination of #JsonUnwrapped and #JsonCreator is not supported yet, so we can generate a solution like this:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.util.UUID;
public class AggregateTest {
static record AggregateId(#JsonProperty("aggregateId") UUID id) {}
static class Aggregate {
#JsonUnwrapped
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
public final AggregateId _aggregateId;
public final String otherField;
#JsonCreator
public Aggregate(#JsonProperty("aggregateId") UUID aggregateId,
#JsonProperty("otherField") String otherField) {
this._aggregateId = new AggregateId(aggregateId);
this.otherField = otherField;
}
}
public static void main(String[] args) throws JsonProcessingException {
String rawJson =
"{\"aggregateId\": \"1f61aede-83dd-4049-a6ff-337887b6b807\"," +
"\"otherField\": \"İsmail Y.\"}";
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
Aggregate aggregate = objectMapper
.readValue(rawJson, Aggregate.class);
System.out.println(objectMapper
.writeValueAsString(aggregate));
}
}
Here we briefly get rid of the #JsonUnwrapped field.
We get the UUID with the name aggregateId and create an AggregateId record.
Detailed explanations about it:
https://github.com/FasterXML/jackson-databind/issues/1467
https://github.com/FasterXML/jackson-databind/issues/1497

Modify POJO class fields with custom setter or custom annotation (in Spring Boot)

Given a POJO in Spring Boot with several dozen fields of type String which is deserialized by Jackson. For demonstration purposes the following example only contains three fields:
#NoArgsConstructor
public class SomeRequest {
#JsonProperty("field_1")
private String field1;
#JsonProperty("field_2")
private String field2;
#JsonProperty("field_3")
private String field3;
}
I'm looking for a way to override the setter method but only for certain fields, i.e. I'd like to avoid repeating the below code for every affected field. This is doable for a handful number of fields but gets tedious for more than a handful.
public setField2(String field2) {
this.field2 = field2 + "?";
}
My idea was to place an annotation on the field like this:
#NoArgsConstructor
public class SomeRequest {
// ...
#JsonProperty("field_2")
#AppendQuestionMark
private String field2;
// ...
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface AppendQuestionMark {
}
But I'm lacking information on how to "implement" the AppendQuestionMark annotation which would override the field's setter method.
Or am I thinking way too complicated?
You can't change the settermethod's body if that's what you are asking. But you can create a method that will take an object (i.e. SomeRequest) as input and check which fields have your Annotation and change the values for those fields as you want.
For example, I created an annotation AppendStr.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface AppendStr {
public String str();;
}
Then I created another class 'AppendStrImpl` that will handle the implementation. I used the following code -
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class AppendStrImpl {
public void changeFields(Object object) throws Exception {
Class<?> clazz = object.getClass();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
if (field.isAnnotationPresent(AppendStr.class)) {
// get the getter method name from the field name
String fieldName = field.getName();
String getterMethodName =
"get" +
fieldName.substring(0, 1).toUpperCase() +
fieldName.substring(1);
Method getterMethod = clazz.getMethod(getterMethodName);
String returnValue = (String) getterMethod.invoke(object);
String setterMethodName = getterMethodName.substring(0, 1).replace("g", "s")
+ getterMethodName.substring(1);
Method setterMethod = clazz.getMethod(setterMethodName, String.class);
setterMethod.invoke(object, returnValue + getAppendingString(field));
System.out.println((String) getterMethod.invoke(object));
}
}
}
private String getAppendingString(Field field) {
return field.getAnnotation(AppendStr.class)
.str();
}
}
And this is my POJO class -
public class POJO {
#AppendStr(str = "?")
private String filed1;
#AppendStr(str = "!")
private String filed2;
private String filed3;
#AppendStr(str = "+")
private String filed4;
// ... getters and setters
}
Then I called this method from the main method -
POJO pojo = new POJO("a", "b", "c", "d");
AppendStrImpl appendStrImpl = new AppendStrImpl();
try {
appendStrImpl.changeFields(pojo);
} catch (Exception e) {
e.printStackTrace();
}
Now you can make this call with hard coding or you can use #Aspect too if you want.
The github link is here.
Instead of creating a new annotation that appends a question mark to one generic string field in your pojo you can use the already present JsonDeserialize annotation over the string fields you are interested:
#Data
#NoArgsConstructor
public class SomeRequest {
#JsonProperty("field_1")
private String field1;
#JsonProperty("field_2")
//here the custom deserializer appends the question mark character
#JsonDeserialize(using = StringAppendQuestionMarkDeserializer.class)
private String field2;
}
In your spring boot project you can register the custom deserializer with the JsonComponent annotation like below:
#JsonComponent
public class StringAppendQuestionMarkDeserializer extends JsonDeserializer<String> {
#Override
public String deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
return node.asText() + "?";
}
}
A spring boot test example using the custom deserializer:
#JsonTest
class CorespringApplicationTests {
#Test
void testDeserialize() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
SomeRequest request = mapper.readValue("{\"field_1\":\"value1\",\"field_2\":\"value2\"}", SomeRequest.class);
System.out.println(request); //<-- SomeRequest(field1=value1, field2=value2?)
}
}
Something like the following should do the trick:
#Aspect
#Component
public class AppendQuestionMarkAspect {
#Around("#annotation(AppendQuestionMark)")
public Object appendQuestionMark(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] arguments = joinPoint.getArgs();
return joinPoint.proceed(new Object[] {((String) arguments[0]) + "?"});
}
}
Of course, it would be advisable to check that only one argument exists and that it is, in fact, a String. Or you can also define the pointcut as to be applied only to methods starting with set. But the essence of the code is there.

How to tell jackson to ignore some fields when deserialising

I am calling an endpoint in which it returns an object. In this object it contains some fields and also a field of another type of object.
E.g.
class ResponseObject{
private final boolean success;
private final String message;
private final DifferentType different;
}
I am calling the endpoint via RestTemplate:
private LogonResponseMessage isMemberAuthenticated(UserCredentialDomain userCredentialDomain)
{
RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForObject(
"http://abc.local:8145/xyz/member/authenticate/{memberLoginName}/{password}", ResponseObject.class,
userCredentialDomain.getUsername(), userCredentialDomain.getPassword());
}
So my consuming app is giving this error:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `fgh.thg.member.DifferentTypeABC` (no Creators, like default constructor, exist): cannot deserialize from Object v
alue (no delegate- or property-based Creator)
I am aware that it's telling me to put a default constructor in the DifferentTypeABC class in order for jackson to deserialise it but i can't do that easily as I'll need to update the dependency on apps that the DifferentTypeABC is in across several different repos.
So i was wondering if there is a way of configuring RestTemplate or jackson on the consuming app so that it ignores attempting to deserialise objects if it doesn't contain a default constructor? To be honest, I am pretty much interested in the success and message fields on the response object.
Another way to solve the problem is by enhancing DifferentType in this module using Jackson creator Mixin. Below is an example:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(mapper.getVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
mapper.addMixIn(DifferentType.class, DifferentTypeMixin.class);
String raw = "{\"message\": \"ok\", \"differentType\": {\"name\": \"foo bar\"}}";
ResponseObject object = mapper.readValue(raw, ResponseObject.class);
System.out.println(mapper.writeValueAsString(object));
}
#Data
static class ResponseObject {
private String message;
private DifferentType differentType;
}
static class DifferentType {
private String name;
public DifferentType(String name) {
this.name = name;
}
}
public static abstract class DifferentTypeMixin {
#JsonCreator
DifferentTypeMixin(#JsonProperty("name") String name) {
}
}
}

Can anyone suggest some best practices for supporting partial representations in restful service

I am implementing restful API where i would like the caller to specify the specific properties that one likes to be serialized something like /dogs/1?fields=name,breed. So if the dog has many more properties other than name and breed and I am using a standard serialization library like jackson how can i tell jackson to ignore the rest of the properties while serializing in runtime?
In other words what is the way to tell jackson to dynamically ignore some properties at runtime?
You did not specify version of Jackson library, so I assume that you are using 2.2.0 or newest. In Jackson this is not easy task to do but we can do it, for example, with BasicClassIntrospector. Below source code shows how it should look like. For example we have one POJO class:
class User {
private String name;
private String surname;
// getters/setters
}
We have to define new BasicClassIntrospector which returns bean definition object only with this fields which we need:
class ExclusionAnnotationIntrospector extends BasicClassIntrospector {
private Collection<String> properties;
public ExclusionAnnotationIntrospector(Collection<String> properties) {
this.properties = properties;
}
#Override
public BasicBeanDescription forSerialization(SerializationConfig serializationConfig,
JavaType javaType, MixInResolver mixInResolver) {
BasicBeanDescription beanDesc = super.forSerialization(serializationConfig, javaType,
mixInResolver);
deleteUnwantedProperties(beanDesc);
return beanDesc;
}
private void deleteUnwantedProperties(BasicBeanDescription beanDesc) {
List<BeanPropertyDefinition> beanProperties = new ArrayList<BeanPropertyDefinition>(
beanDesc.findProperties());
for (BeanPropertyDefinition bpd : beanProperties) {
String name = bpd.getName();
if (!properties.contains(name)) {
beanDesc.removeProperty(name);
}
}
}
}
Now, we have to create new ObjectMapper class with above introspector. We have to define properties we need in constructor:
class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper(Collection<String> properties) {
this._serializationConfig = getSerializationConfig().with(
new ExclusionAnnotationIntrospector(properties));
}
}
And, finally, example usage:
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.introspect.BasicBeanDescription;
import com.fasterxml.jackson.databind.introspect.BasicClassIntrospector;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
public class JacksonProgram {
public static void main(String[] args) throws Exception {
User user = new User();
user.setName("Tom");
user.setSurname("Garnier");
CustomObjectMapper onlyNameSerializer = new CustomObjectMapper(toSet("name"));
CustomObjectMapper onlySurnameSerializer = new CustomObjectMapper(toSet("surname"));
CustomObjectMapper fullUserSerializer = new CustomObjectMapper(toSet("name", "surname"));
System.out.println(onlyNameSerializer.writeValueAsString(user));
System.out.println(onlySurnameSerializer.writeValueAsString(user));
System.out.println(fullUserSerializer.writeValueAsString(user));
}
private static Set<String> toSet(String... params) {
HashSet<String> set = new HashSet<String>();
for (String item : params) {
set.add(item);
}
return set;
}
}
Above program prints:
{"name":"Tom"}
{"surname":"Garnier"}
{"name":"Tom","surname":"Garnier"}
I'd recommend to take a look at http://flexjson.sourceforge.net/
(in particular section Working With Includes/Excludes)
Example:
public String toJsonWithIncludes(Object obj, String...includes) {
return new JSONSerializer()
.include(includes)
.exclude("data.*","*.class")
.serialize(obj);
}
...
toJsonWithIncludes(obj, "name","breed");

Categories

Resources