Reputation:
I am using Jackson for JSON (de)serialization in conjunction with Spring. However I am having an issue with a field being twice in some cases.
I have an abstract class:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "mimeType")
@JsonSubTypes({
@JsonSubTypes.Type(value = ImageBookmarkJsonModel.class, name = "image/jpeg"),
@JsonSubTypes.Type(value = EpubBookmarkJsonModel.class, name = "application/epub+zip")
})
public abstract class AbstractBookmarkJsonModel extends AbstractJsonModel {
protected String mimeType;
// Removed other fields for brevity
public String getMimeType() {
return mimeType;
}
public void setMimeType(String mimeType) {
this.mimeType = mimeType;
}
@Override
public String toString() {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(this);
} catch (IOException e) {
throw new IllegalStateException("Cannot convert object of type " + this.getClass().toString() + " to JSON", e);
}
}
}
And a concrete class extend the abstract:
public class EpubBookmarkJsonModel extends AbstractBookmarkJsonModel {
private static final long serialVersionUID = 1L;
// Removed other fields for brevity
public EpubBookmarkJsonModel() {
this.mimeType = "application/epub+zip";
}
}
The problem is that when I serialize this JSON, I get a duplicate mimeType
field:
{
"mimeType": "application/epub+zip",
"mimeType": "application/epub+zip",
"userId": 24,
"acid": "ACID-000000000029087",
"added": "2013-08-14T12:02:17Z",
"epubBookmarkId": 34,
"cfi": "epubcfi(/6/4!/2/68)",
"context": "CONTEXT"
}
I have tried using the recommendation of previous answers to use the @JsonAutoDetect
annotation to specify that only fields on a class should be used as well as setting the same on the ObjectMapper
, however this does not fix the problem.
Annotation:
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
setterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
ObjectMapper:
ObjectMapper mapper = new ObjectMapper();
mapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE);
Upvotes: 59
Views: 34907
Reputation: 954
I solved this by using the JsonTypeInfo.As.EXISTING_PROPERTY
in the @JsonTypeInfo annotation.
Upvotes: 78
Reputation: 23
For me, duplicate fields in JSON occurred when I accidently had same name for field and JSON property like .
@JsonProperty("Feild1") private String Field1;
I fixed it like this
@JsonProperty("Feild1") private String field1;
Upvotes: 0
Reputation: 41
Had the same problem. We're using Lombok, and I got this work with @JsonProperty access:
@Getter
@Setter
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY) // Prevents duplication when serializing to JSON (subtype discriminator property)
private RuleType ruleType;
I like this solution, since:
And yes, need visible=true so that the property will get populated. In our case, it's an Enum with some behavior dispatching, so we certainly need it for our purposes, and it's great to be able to use it for Json subtype discrimination as well.
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "ruleType",
visible = true)
Upvotes: 2
Reputation: 87
This case probably just with legacy versions of jacson lib
and it's simply resolved by moving yours @JsonProperty(value = "Your_CUSTOM_Name")
from fields to getters.
Upvotes: 2
Reputation: 740
I was having this exact same problem with the duplicate output. I found a solution that did not involve another property, and allowed me to not remove the original property. First, I set the visible flag to true for JsonTypeInfo. Then, I added a JsonIgnore annotation to the property declaration and the getter (but not the setter). This is so far outputting the JSON correctly with only one key for the type property.
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "mimeType")
@JsonSubTypes({
@JsonSubTypes.Type(value = ImageBookmarkJsonModel.class, name = "image/jpeg"),
@JsonSubTypes.Type(value = EpubBookmarkJsonModel.class, name = "application/epub+zip")
})
public abstract class AbstractBookmarkJsonModel extends AbstractJsonModel {
@JsonIgnore
@JsonProperty("mimeType")
protected String mimeType;
@JsonIgnore
@JsonProperty("mimeType")
public String getMimeType() {
return mimeType;
}
@JsonProperty("mimeType")
public void setMimeType(String mimeType) {
this.mimeType = mimeType;
}
}
To note, this is with fasterxml jackson jackson-databind 2.1.1
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.1.1</version>
</dependency>
Upvotes: 13
Reputation: 4901
This behaviour is caused by the annotations placed on class AbstractBookmarkJsonModel
:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "mimeType")
@JsonSubTypes({
@JsonSubTypes.Type(value = ImageBookmarkJsonModel.class, name = "image/jpeg"),
@JsonSubTypes.Type(value = EpubBookmarkJsonModel.class, name = "application/epub+zip")
})
@JsonTypeInfo
tells Jackson to serialize the logical type name (JsonTypeInfo.Id.NAME
) as a property (JsonTypeInfo.As.PROPERTY
) with name mimeType
(property = "mimeType"
). With @JsonSubTypes.Type
you assign the logical name application/epub+zip
to EpubBookmarkJsonModel
.
When it comes to serialization, Jackson serializes the logical name as a property mimeType = "application/epub+zip"
then the properties of the object among them
mimeType
which happens to have the same value as the logical name application/epub+zip
(assigned in the constructor).
I think mimeType
should be changed to objectType
in the @JsonTypeInfo
annotation or even better to remove the mimeType
field since Jackson will take care of that through type info serialization.
Upvotes: 9