ObjectMapper adds extra Field to the JSON String - java

I have the following problem.
Here is my Accident class and the CommonDomainEntity class:
#NoArgsConstructor
#AllArgsConstructor
#Data
public class Accident extends CommonDomainObject {
private String status;
private Date accidentDate;
private String name;
}
#Data
public abstract class CommonDomainObject {
public Long id;
public boolean isNew() {
return null == getId();
}
}
In my test class I am calling the following:
String exp = objMapper.writeValueAsString(accidents);
System.out.println(exp);
ResponseEntity<String> res = restTemplate.getForEntity("/accidents", String.class);
assertEquals(HttpStatus.OK, res.getStatusCode());
JSONAssert.assertEquals(exp, res.getBody(), false);
It throws the following error:
java.lang.AssertionError: [id=2]
Expected: new
but none found
; [id=3]
Expected: new
but none found
I already tried to out print the object exp to see whats in it, as well as I tried to print whats inaccidents`.
As you see in the console logs, for some reason in the exp object there is a new=false field, and I can`t figure out where this is from.
This right here is what is in my accidents List
Accident(status=pending, accidentDate=null, name=Name),
Accident(status=closed, accidentDate=null, name=Name)]
And this is my exp object as JSON
[{"id":2,"status":"pending","accidentDate":null,"name":"Name","new":false},
{"id":3,"status":"closed","accidentDate":null,"name":"Name","new":false}]

Your CommonDomainObject.isNew() method in the abstract class is evaluated as a JSON field by ObjectMapper. You must exclude it using jackson anotations.
public abstract class CommonDomainObject {
...
#JsonIgnore
public boolean isNew() {
return null == getId();
}
}
See:
Want to hide some fields of an object that are being mapped to JSON by Jackson
https://github.com/FasterXML/jackson-annotations/wiki/Jackson-Annotations
https://github.com/FasterXML/jackson-annotations
Your MCVE would be:
Call objMapper.writeValueAsString()
Check why the resulting JSON string representation contains the new field
All the other code is reduntant for the reproduction of your issue :)

Related

How can I have different names for the same field in different APIs?—Jackson

I have an API whose response is as follows:
{
ruleId:”123”,
ruleName:”Rule1”
}
Now I am introducing a new Api which exactly has these fields but the response should not have name as ruleId ,ruleName but as id,name:
{
id:”123”,
name:”Rule1”
}
I should change in such a way so that the previous Api response should not be impacted.
Thought to use JsonProperty /JsonGetter but it will change the previous Api response as well.
Is there any way that I can have 2 getters for the same field and then use one getter for previous Apis and other one for my purpose? (My concern is only when converting Pojo to JSON)
Can anyone help?
Since you want serialize the object differently in different cases, using jackson mix-in is preferred.
Here is example how to do that.
If your pojo looks something like this:
public class CustomPojo {
private String ruleId;
private String ruleName;
public String getRuleId() {
return ruleId;
}
public void setRuleId(String ruleId) {
this.ruleId = ruleId;
}
public String getRuleName() {
return ruleName;
}
public void setRuleName(String ruleName) {
this.ruleName = ruleName;
}
}
First, you need to create one interface (or class) like this:
import com.fasterxml.jackson.annotation.JsonProperty;
public interface CostomPojoMixin {
#JsonProperty("Id")
String getRuleId();
#JsonProperty("name")
String getRuleName();
}
This interface will be used to rename fields ruleId and ruleName during serilization.
Then when you have all this setup you can write controller method and customize ObjectMapper:
#GetMapping(value = "/test/mixin")
public String testMixin() throwsJsonProcessingException {
CostomPojo cp = new CostomPojo();
cp.setRuleId("rule");
cp.setRuleName("name");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn(CustomPojo.class, CostomPojoMixin.class);
String json = objectMapper.writeValueAsString(cp);
return json;
}
This endpoint should return response like this:
{"Id":"rule","name":"name"}

Override jackson #JsonValue from superclass

I have an interface (that already contains a Jackson annotation):
interface Interface {
#JsonValue
String fieldA();
String fieldB();
}
which I cannot modify, and a class that implements this interface:
class Impl implements Interface {
String fieldA;
String fieldB;
public Impl(String fieldA, String fieldB) {
this.fieldA = fieldA;
this.fieldB = fieldB;
}
#Override
#JsonSerialize
public String fieldA() {
return fieldA;
}
#Override
#JsonSerialize
public String fieldB() {
return fieldB;
}
}
Now, when I serialize the Impl class I would expect that the generated Json would have both fields (fieldA and fieldB) present.
This is not the case:
#Test
void should_serialize_both_fields() throws JsonProcessingException {
// Given
ObjectMapper mapper = new ObjectMapper();
Impl example = new Impl("test", "test");
String expected = "{\"fieldA\": \"test\", \"fieldB\": \"test\"}";
// When
String json = mapper.writeValueAsString(example);
// Then
org.assertj.core.api.Assertions.assertThat(json).isEqualTo(expected);
}
In this test the resulting json is "test" instead of {"fieldA": "test", "fieldB": "test"}:
org.opentest4j.AssertionFailedError:
Expecting:
<""test"">
to be equal to:
<"{"fieldA": "test", "fieldB": "test"}">
but was not.
The problem comes from the already present #JsonValue annotation on the interface, which I cannot modify. Also, if I try to annotate another method in Impl then I get this exception from jackson:
com.fasterxml.jackson.databind.JsonMappingException: Problem with definition of [AnnotedClass com.actility.m2m.commons.service.error.InternalErrorCodeImplTest$Impl]: Multiple 'as-value' properties defined ([method com.actility.m2m.commons.service.error.InternalErrorCodeImplTest$Impl#fieldB(0 params)] vs [method com.actility.m2m.commons.service.error.InternalErrorCodeImplTest$Impl#fieldA(0 params)])
Is there any way to achieve this?
Going by the docs, you should be able to set a "false" JSON value in the subclass:
Boolean argument is only used so that sub-classes can "disable" annotation if necessary.
I guess that already tells you all you need to know, but here's what it would look like:
class Impl implements Interface {
//...
#Override
#JsonSerialize
#JsonValue(false) //...disables inherited annotation
public String fieldA() {
return fieldA;
}
// ...
}

Custom JSON Serialization Wrapper for Any Object

I use external application which expects an Object that Serializable from me like his function:
externalFunction(Object input);
So I should give that function an input that will be correctly serialized into JSON when the method is invoked (not controlled by me).
But I don't know how data is structured since I receive input from another external application dynamically. So case like this:
1. Get data from 3rd party
2. MyApp should annotate data for Json Serialization
3. Send data to 3rd party as input
4. Response will be produced as JSON
How can I achieve this? How can I give input to the function that is correctly serialized when the function is invoked?
What I tried so far:
So first thing I try is wrap data with some Wrapper like:
public class JsonWrapper<T> implements Serializable
{
public T attributes;
public JsonWrapper( T attributes )
{
this.attributes = attributes;
}
#JsonValue
public T getAttributes( )
{
return attributes;
}
}
So I wrap data like ->
data = getFromThirdParty();
wrapped = new JsonWrapper<>(data);
externalFunction(wrapped);
But it produces a response with "attributes" field which I don't want. Also I tried to use #JsonUnwrapped public T attributes; but the result is same.
I don't want this:
{
"attributes": {
... some fields/values that I don't know, get from 3rd party
}
}
I want like this:
{
... some fields/values that I don't know, get from 3rd party
}
The #JsonUnwrapped annotation doesn't work when T is a Collection (see this answer from the Jackson's creator). But the #JsonValue annotation actually does the trick:
public class JsonWrapper<T> {
#JsonValue
private T value;
public JsonWrapper(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
If you use Lombok, you can have:
#Getter
#AllArgsConstructor
public class JsonWrapper<T> {
#JsonValue
private T value;
}
Example
Consider the following class:
#Data
#AllArgsConstructor
public class Person {
private String firstName;
private String lastName;
}
When serializing an Person instance, the following result JSON is produced:
ObjectMapper mapper = new ObjectMapper();
JsonWrapper<?> wrapper = new JsonWrapper<>(new Person("John", "Doe"));
String json = mapper.writeValueAsString(wrapper);
{"firstName":"John","lastName":"Doe"}
When serializing a list of Person instances, the following result JSON is produced:
ObjectMapper mapper = new ObjectMapper();
JsonWrapper<?> wrapper = new JsonWrapper<>(
Arrays.asList(
new Person("John", "Doe"),
new Person("Jane", "Poe")
));
String json = mapper.writeValueAsString(wrapper);
[{"firstName":"John","lastName":"Doe"},{"firstName":"Jane","lastName":"Poe"}]

map json string to enum

I have the following class and enums:
import lombok.Data;
// other imports...
#Data
public class MapTest{
private MyFirstEnum myFirstEnum;
private MySecondEnum mySecondEnum;
}
public enum MyFirstEnum{
MY_FIRST_ENUM1,
MY_FIRST_ENUM2
}
public enum MySecondEnum {
MY_SECOND_ENUM1,
MY_SECOND_ENUM2
}
and this spring controller:
#PostMapping("/testMap")
#ResponseBody
public void TestMap(#RequestBody MapTest mapTest){
}
Since an enum can be looked up by its name what I would like to do is to post a json to the controller and that the appropriate props will be serialized by their name:
{
"myFirstEnum": "MY_FIRST_ENUM1",
"mySecondEnum": "MY_SECOND_ENUM2"
}
I've tried to set up a #JsonDeserialize but i couldn't get the type of the enum inside the overridden function:
// what type should i use here?
public static class StringToEnum extends JsonDeserializer<???> {
// how do i get the type of the current enum?
#Override
public ??? deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
??? res = Enum.valueOf(p.getText());
return res;
}
}
Update:
I've failed to mention that i'm using lombok's #data attribute for automatically generating getters and setters, which doesn't work well with enum bindings (not sure why).
I guess that laziness comes with a price.
It should be serialized automatically via jackson but you can force it via #JsonCreator
Redefine your enums as
public enum MyFirstEnum{
MY_FIRST_ENUM1,
MY_FIRST_ENUM2;
#JsonCreator
public static MyFirstEnum fromString(String raw) {
return MyFirstEnum.valueOf(raw.toUpperCase());
}
}
Similarly define your second enum as well in the similar manner.
Imp Note (Mandatory)
MapTest should have public setter / getter for both enums (if declared private, preferred), or declare them public (should be avoided, not preferred)

Jackson + Builder Pattern?

I'd like Jackson to deserialize a class with the following constructor:
public Clinic(String name, Address address)
Deserializing the first argument is easy. The problem is that Address is defined as:
public class Address {
private Address(Map<LocationType, String> components)
...
public static class Builder {
public Builder setCity(String value);
public Builder setCountry(String value);
public Address create();
}
}
and is constructed like this: new Address.Builder().setCity("foo").setCountry("bar").create();
Is there a way to get key-value pairs from Jackson in order to construct the Address myself? Alternatively, is there a way to get Jackson to use the Builder class itself?
As long as you are using Jackson 2+, then there is now built in support for this.
First you need to add this annotation to your Address class:
#JsonDeserialize(builder = Address.Builder.class)
Then you need to add this annotation to your Builder class:
#JsonPOJOBuilder(buildMethodName = "create", withPrefix = "set")
You can skip this second annotation if you are happy to rename your Builder's create method to build, and your Builder's setters to be prefixed to with, instead of set.
Full example:
#JsonDeserialize(builder = Address.Builder.class)
public class Address
{
private Address(Map<LocationType, String> components)
...
#JsonPOJOBuilder(buildMethodName = "create", withPrefix = "set")
public static class Builder
{
public Builder setCity(String value);
public Builder setCountry(String value);
public Address create();
}
}
The answer from #Rupert Madden-Abbott works. However, if you have a non-default constructor, e.g.,
Builder(String city, String country) {...}
Then you should annotate the parameters as below:
#JsonCreator
Builder(#JsonProperty("city") String city,
#JsonProperty("country") String country) {...}
A solution which was suitable for me in this case (I used "Lombok" builder annotation).
#Getter
#Builder(builderMethodName = "builder")
#NoArgsConstructor(access = AccessLevel.PRIVATE)
#AllArgsConstructor(access = AccessLevel.PRIVATE)
#JsonAutoDetect(
fieldVisibility = JsonAutoDetect.Visibility.ANY,
creatorVisibility = JsonAutoDetect.Visibility.ANY
)
I hope would be useful for u too.
I ended up implementing this using the #JsonDeserialize as follows:
#JsonDeserialize(using = JacksonDeserializer.class)
public class Address
{...}
#JsonCachable
static class JacksonDeserializer extends JsonDeserializer<Address>
{
#Override
public Address deserialize(JsonParser parser, DeserializationContext context)
throws IOException, JsonProcessingException
{
JsonToken token = parser.getCurrentToken();
if (token != JsonToken.START_OBJECT)
{
throw new JsonMappingException("Expected START_OBJECT: " + token, parser.getCurrentLocation());
}
token = parser.nextToken();
Builder result = new Builder();
while (token != JsonToken.END_OBJECT)
{
if (token != JsonToken.FIELD_NAME)
{
throw new JsonMappingException("Expected FIELD_NAME: " + token, parser.getCurrentLocation());
}
LocationType key = LocationType.valueOf(parser.getText());
token = parser.nextToken();
if (token != JsonToken.VALUE_STRING)
{
throw new JsonMappingException("Expected VALUE_STRING: " + token, parser.getCurrentLocation());
}
String value = parser.getText();
// Our Builder allows passing key-value pairs
// alongside the normal setter methods.
result.put(key, value);
token = parser.nextToken();
}
return result.create();
}
}
There is no support currently for builder pattern, although it has been requested quite a while ago (and finally Jira issue http://jira.codehaus.org/browse/JACKSON-469 was filed) -- it is something that may be added for 1.8 release if there is enough demand (make sure to vote at Jira!). It is a reasonable additional feature, and only delayed by amount of time developers have. But I think it would be great addition.
This worked for me: #NoArgsConstructor
The only drawback of this, is that one can do = new ADTO() again.
But, hey, I dont like de code police anyhow, telling me how to use someones code :-)
So, use my POJO DTOS the way you like it. With or without builder. I suggest: do it with a Builder, but be my guest...
#Data
#Builder
//Dont forget this! Otherwise no Jackson serialisation possible!
#NoArgsConstructor
#AllArgsConstructor
public class ADTO {
.....
}

Categories