Change field case with an ObjectMapper - java

I think I need to create a specialist ObjectMapper and cannot find any sample code to start the process.
The creator of the JSON is using .Net and public properties and therefore uses field names with an uppercase initial. I am parsing the JSON into POJOs so I would like to use a lowercase initial.
At their end:
public class Facet
{
public string Name { get; set; }
public string Value { get; set; }
}
At my end I must therefore have:
public class Facet {
public String Name;
public String Value;
}
I would much prefer:
public class Facet {
public String name;
public String value;
}
Am I right that this could be done with an ObjectMapper?

Your first issue can be addressed very simply with the #JsonProperty annotation:
// java-side class
public class Facet
{
#JsonProperty("Name")
public String name;
#JsonProperty("Value")
public String value;
}
Now the ObjectMapper will match up the differently-cased field names. If you don't want to add annotations into your classes, you can create a Mix-in class to stand in for your Facet:
public class FacetMixIn
{
#JsonProperty("Name")
public String name;
#JsonProperty("Value")
public String value;
}
objectMapper.getDeserializationConfig().addMixInAnnotations(Facet.class, FacetMixIn.class);
This will achieve the same thing, without requiring additional annotations in your Facet class.

Instead of annotating each field, the Jackson ObjectMapper can be configured to use a built-in or custom PropertyNamingStrategy, to apply a consistent translation between Java property/field names and JSON element names.
For example:
myObjectMapper.setPropertyNamingStrategy(PascalCaseStrategy);

This problem could be solved from Jackson 2.5.0 like this:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
From the javadoc:
com.fasterxml.jackson.databind.MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES
Feature that will allow for more forgiving deserialization of incoming
JSON. If enabled, the bean properties will be matched using their
lower-case equivalents, meaning that any case-combination (incoming
and matching names are canonicalized by lower-casing) should work.
Note that there is additional performance overhead since incoming
property names need to be lower-cased before comparison, for cases
where there are upper-case letters. Overhead for names that are
already lower-case should be negligible however.
Feature is disabled by default.
Since:
2.5

Just a quick update as I was looking for same answer and a code snippet objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);

Since v 2.13 use builder:
XmlMapper xmlMapper = (XmlMapper) getObjectMapper();
private ObjectMapper getObjectMapper() {
return XmlMapper.builder()
.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
.build();
}

Related

How to use Jackson to deserialize external Lombok builder class

I have a 3rd party Lombok builder POJO, one that I cannot modify, that I want to serialize using jackson. Notably it does not have a NoArgsConstructor.
#Data
#Builder
public class ExternalClass {
private String name;
private String data;
// etc.
}
On the surface this would appear to be simple, but it is incredibly frustrating in practice as each possible option seems to be counteracted by a different complication. In essence, I'm having trouble getting an external Lombok builder to work with a jackson mixin.
Lombok produces fluent setters of the style .name(String name) while Jackson's built-in builder deserializer expects .withName(String name). Lombok documentation, and recipes elsewhere such as here suggest using #JsonDeserialize(builder=ExternalClass.ExternalClassBuilder.class) in conjunction with #JsonPOJOBuilder(withPrefix="") on a predeclared inner stub builder. But this is not possible because the Lombok class is in an external library.
Applying these annotations to a mixin has no effect.
#JsonDeserialize(ExternalClass.ExternalClassBuilder.class)
public abstract class ExternalClassMixin {
#JsonPOJOBuilder(withPrefix="")
public static ExternalClassBuilder {
}
}
The only approach I've found that works is to leverage the package-access AllArgsConstructor created by #Builder and populate the mixin with the following constructor
public abstract class ExternalClassMixin {
#JsonCreator public ExternalClassMixin(
#JsonProperty("name") String name,
#JsonProperty("data") String data,
// etc.
) {}
}
This is obviously not desirable as it requires iterating and hard-coding every class property explicitly, making the mixin fragile to any change in the external POJO.
My question is - is there a robust, maintainable way to serialize this external builder class using Jackson without modifying it, using either a mixin or maybe a full blown deserializer?
Update
I implemented the excellent answer by #jan-rieke, including the suggestion to use reflection to seek out the inner builder class.
...
public Class<?> findPOJOBuilder(AnnotatedClass ac) {
Class<?> innerBuilder;
try {
innerBuilder = Class.forName(ac.getName()+"$"+ac.getRawType().getSimpleName()+"Builder");
log.info("Builder found: {}", ac.getName());
return innerBuilder;
} catch( ClassNotFoundException e ) {
return super.findPOJOBuilder(ac);
}
}
You can customize your ObjectMapper as follows:
ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
#Override
public Class<?> findPOJOBuilder(AnnotatedClass ac) {
if (ExternalClass.class.equals(ac.getRawType())) {
return ExternalClass.ExternalClassBuilder.class;
}
return super.findPOJOBuilder(ac);
}
#Override
public Value findPOJOBuilderConfig(AnnotatedClass ac) {
if (ac.hasAnnotation(JsonPOJOBuilder.class)) {
return super.findPOJOBuilderConfig(ac);
}
return new JsonPOJOBuilder.Value("build", "");
}
});
This will
explicitly configure that deserialization for ExternalClass uses its builder, and
set the default prefix for builder setter methods to "" (except when the #JsonPOJOBuilder annotation is present).
If you do not want to list all external classes explicitly in findPOJOBuilder(), you can of course programmatically look into the class to check whether it has a inner class that looks like a builder.
This can be accomplished by creating two mixins: one for ExternalClass (specifying the builder to use) and one for ExternalClass.ExternalClassBuilder (specifying the lack of a prefix in the builder methods).
#JsonDeserialize(builder = ExternalClass.ExternalClassBuilder.class)
public interface ExternalClassMixin {
}
#JsonPOJOBuilder(withPrefix="")
public interface ExternalClassBuilderMixin {
}
This serializes and deserializes the JSON in the desired manner:
String json = "{\"name\": \"The Name\", \"data\": \"The Data\"}";
ObjectMapper mapper = new ObjectMapper()
.addMixIn(ExternalClass.class, ExternalClassMixin.class)
.addMixIn(ExternalClass.ExternalClassBuilder.class, ExternalClassBuilderMixin.class);
System.out.println(mapper.readValue(json, ExternalClass.class));
System.out.println(mapper.writeValueAsString(mapper.readValue(json, ExternalClass.class)));
Output:
ExternalClass(name=The Name, data=The Data)
{"name":"The Name","data":"The Data"}

Kotlin: Change the json property name depending on the #JsonView

I am trying to use Jackson to serialize the same DTO object in 2 different ways, depending on the #JsonView.
I want to use 2 different names for the same field. In one case I want to name the json property myField (just like the class field name), in the other I want it to be named myInternalApiField.
As a result I would like to see outcomes similar to the presented below:
Usage 1 (External API View):
{
"myField": "value1",
"myOtherField": "otherValue"
}
Usage 2 (Internal API View):
{
"myInternalApiField": "value1",
"myOtherField": "otherValue"
}
In my implementation in Java to achieve that I used the combination of custom getters, setters and #JsonView annotation as below:
public class CustomDTO {
#JsonView(Views.ExternalApiView)
private String myField;
// Other fields here
#JsonView(Views.InternalApiView)
public String getMyInternalApiField() { return myField; }
#JsonView(Views.InternalApiView)
public void setMyInternalApiField(String value) { this.myField = value; }
#JsonView(Views.ExternalApiView)
public String getMyField() { return myField; }
#JsonView(Views.ExternalApiView)
public void setMyField(String value) { this.myField = value }
}
However I don't know how to properly achieve the same result in Kotlin.
I was thinking about using something like:
data class CustomDTO(
#get:[JsonView(Views.ExternalApiView) JsonProperty("myField")]
#get:[JsonView(Views.InternalApiView) JsonProperty("myInternalApiField")]
#set:[JsonView(Views.InternalApiView) JsonProperty("myField")]
#set:[JsonView(Views.InternalApiView) JsonProperty("myInternalApiField")]
var myField: String,
val myOtherField: String,
val myDifferentField: String
)
But this is not allowed in Kotlin.
Do you have any suggestions how to utilize the #JsonView in Kotlin in the similar way as I did it in Java?
How about something like:
data class CustomDTO(
#JsonView(ExternalApiView::class)
var myField: String,
val myOtherField: String,
val myDifferentField: String
) {
val myExternalField: String
#JsonView(InternalApiView::class)
get() {
return myField
}
}
It looks like there are ways that don't require creating computed properties in the DTO, like:
Using Jackson Mixins
Creating a custom serializer for a particular invocation
Combining a custom serializer with custom annotations
But these have their own complexity, even if that complexity isn't in the DTO class. I'm not sure these are much more appealing to me but you could see if they appeal to you.

How to serialize differently the same properties of the same entity using jackson

Suppose you have this entity:
class Foo{
String propA;
String propB;
}
and you want to serialize for one API like :
{propA: "ola",
propB: "Holla"}
and for another API like :
{fooPropA: "ola",
fooPropB: "Holla"}
How can this be achieved using jackson and using the same entity. Creating 2 different entities is not an option :)
There are several ways in which you can achieve this. You can enable a custom serializer (already covered by #se_vedem), register an annotation introspector which changes the property names for the corresponding class and so on.
However, if you are willing to only add a string prefix to all the property names, then the Jackson property name strategy is probably the best fit. The naming strategy class has the access to the serialized object type information, so you can make a decision whether to change the property name or not.
Here is an example using a custom annotation that defines the prefix:
public class JacksonNameStrategy {
#Retention(RetentionPolicy.RUNTIME)
public static #interface PropertyPrefix {
String value();
}
#PropertyPrefix("foo_")
public static class Foo {
public String propA;
public String propB;
public Foo(String propA, String propB) {
this.propA = propA;
this.propB = propB;
}
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(new MyPropertyNamingStrategyBase());
System.out.println(mapper.writeValueAsString(new Foo("old", "Holla")));
}
private static class MyPropertyNamingStrategyBase extends PropertyNamingStrategy {
#Override
public String nameForField(MapperConfig<?> config,
AnnotatedField field,
String defaultName) {
PropertyPrefix ann = field.getDeclaringClass().getAnnotation(PropertyPrefix.class);
if (ann != null) {
return ann.value() + defaultName;
}
return super.nameForField(config, field, defaultName);
}
}
}
Output:
{"foo_propA":"old","foo_propB":"Holla"}
In your API method you choose between two ObjectMapper instances one with the default naming naming strategy and one with the custom one.
You can achieve this by using modules feature from Jackson.
Basically, each API would have it's own ObjectMapper and they will be configured with different modules. This way you can create 2 serializers for the same class and register them on the appropriate module. More read can be found here http://wiki.fasterxml.com/JacksonFeatureModules
However, be aware that serializers are loaded in a particular order. First it tries to get the annotated ones, if none is found it will try to get those registered from modules. So, for example if you have your class annotated with serializer, then that serializer(FooSerializer) would be chosen instead of the one configured in module(MySecondFooSerializer).
#JsonSerialize(using = FooSerializer.class)
class Foo{
String propA;
String propB;
}
module.addSerializer(Foo.class, new MySecondFooSerializer());

Want to hide some fields of an object that are being mapped to JSON by Jackson

I have a User class that I want to map to JSON using Jackson.
public class User {
private String name;
private int age;
private int securityCode;
// getters and setters
}
I map this to a JSON string using -
User user = getUserFromDatabase();
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(user);
I don't want to map the securityCode variable. Is there any way of configuring the mapper so that it ignores this field?
I know I can write custom data mappers or use the Streaming API but I would like to know if it possible to do it through configuration?
You have two options:
Jackson works on setters-getters of fields. So, you can just remove getter of field which you want to omit in JSON. ( If you don't need getter at other place.)
Or, you can use the #JsonIgnore annotation of Jackson on getter method of that field and you see there in no such key-value pair in resulted JSON.
#JsonIgnore
public int getSecurityCode(){
return securityCode;
}
Adding this here because somebody else may search this again in future, like me. This Answer is an extension to the Accepted Answer
You have two options:
1. Jackson works on setters-getters of fields. So, you can just remove getter of field which you want to omit in JSON. ( If you don't need getter at other place.)
2. Or, you can use the `#JsonIgnore` [annotation of Jackson][1] on getter method of that field and you see there in no such key-value pair in resulted JSON.
#JsonIgnore
public int getSecurityCode(){
return securityCode;
}
Actually, newer version of Jackson added READ_ONLY and WRITE_ONLY annotation arguments for JsonProperty. So you could also do something like this.
#JsonProperty(access = Access.WRITE_ONLY)
private String securityCode;
instead of
#JsonIgnore
public int getSecurityCode(){
return securityCode;
}
you also can gather all properties on an annotation class
#JsonIgnoreProperties( { "applications" })
public MyClass ...
String applications;
If you don't want to put annotations on your Pojos you can also use Genson.
Here is how you can exclude a field with it without any annotations (you can also use annotations if you want, but you have the choice).
Genson genson = new Genson.Builder().exclude("securityCode", User.class).create();
// and then
String json = genson.serialize(user);
Field Level:
public class User {
private String name;
private int age;
#JsonIgnore
private int securityCode;
// getters and setters
}
Class Level:
#JsonIgnoreProperties(value = { "securityCode" })
public class User {
private String name;
private int age;
private int securityCode;
}
if you are using GSON you have to mark the field/member declarations as #Expose and use the GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()
Don't forget to mark your sub classes with #Expose otherwise the fields won't show.
I suggest you use this.
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private int securityCode;
This allows you to set the value of securityCode(especially if you use lombok #Setter) and also prevent the field from showing up in the GET request.
I had a similar case where I needed some property to be deserialized (JSON to Object) but not serialized (Object to JSON)
First i went for #JsonIgnore - it did prevent serialization of unwanted property, but failed to de-serialize it too. Trying value attribute didn't help either as it requires some condition.
Finally, working #JsonProperty with access attribute worked like a charm.

Jackson vs Gson for simple deserialisation

For parsing JSON like this twitter API users/show response I've been using Jackson and Gson Java libraries as candidates to do this work. I'm only interested in a small subset of properties of the JSON so Gson was nice because of its very concise syntax but I'm losing an internal battle to continue to use Gson as Jackson is already used elsewhere in our application and it has documented better performance (which I concede are both good reasons to lose Gson).
For a POJO like
public class TwitterUser {
private String id_str;
private String screen_name;
public String getId_str() {
return id_str;
}
public void setId_str(String id_str) {
this.id_str = id_str;
}
public String getScreen_name() {
return screen_name;
}
public void setScreen_name(String screen_name) {
this.screen_name = screen_name;
}
}
The only code for Gson needed to build this is one line,
TwitterUser user = new Gson().fromJson(jsonStr, TwitterUser.class);
That's pretty nice to me; scales well and is opt-in for the properties you want. Jackson on the other hand is a little more laborious for building a POJO from selected fields.
Map<String,Object> userData = new ObjectMapper().readValue(jsonStr, Map.class);
//then build TwitterUser manually
or
TwitterUser user = new ObjectMapper().readValue(jsonStr, TwitterUser.class);
//each unused property must be marked as ignorable. Yikes! For 30 odd ignored fields thats too much configuration.
So after that long winded explanation, is there a way I can use Jackson with less code than is demonstrated above?
With Jackson 1.4+ you can use the class-level #JsonIgnoreProperties annotation to silently ignore unknown fields, with ignoreUnknown set to true.
#JsonIgnoreProperties(ignoreUnknown = true)
public class TwitterUser {
// snip...
}
http://wiki.fasterxml.com/JacksonAnnotations
http://wiki.fasterxml.com/JacksonHowToIgnoreUnknown

Categories

Resources