Reputation: 6637
In my "quest" to parse lottieFile and dotLottie JSONs into Java objects and back to identical lottieFiles (see https://lottie4j.com/), I discovered a strange effect of @JsonSubTypes.
Java 17 + Jackson 2.14.1
As I'm making an implementation on top of the Lottie data model, it's not possible to make a change in the JSON format.
It seems that the following code, causes the Integer value of a
to become a String when converting back to JSON.
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "a")
@JsonSubTypes({
@JsonSubTypes.Type(names = {"0", "0.0"}, value = FixedBezier.class),
@JsonSubTypes.Type(names = {"1", "1.0"}, value = AnimatedBezier.class)
})
This is the test result:
Original: {"a": 0, "d": "test"}
Generated: {"a": "0", "d": "test"}
Even when using
@JsonSerialize(using = IntegerSerializer.class)
or
@JsonSerialize(using = AnimatedSerializer.class)
With or without these @JsonSerialize
, "a": "0"
is generated instead of "a": 0
. Am I missing some configuration to force the JsonSubType value to be stored as a number in the JSON? How can this be achieved?
This is my full test code, with both the @JsonSerialize
commented to easily switch between different possible solutions...
import com.fasterxml.jackson.annotation.*;
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 java.io.IOException;
public class BezierTest {
private static final ObjectMapper mapper = new ObjectMapper();
public static void main(String[] args) {
String[] jsons = new String[]{
// jsonLottieFilesFixed
"{\"a\":0,\"d\":\"test\"}",
// jsonLottieFilesAnimated
"{\"a\":1,\"d\":\"test\"}",
// dotLottieFilesFixed
"{\"a\":0.0,\"d\":\"test\"}",
// dotLottieFilesAnimated
"{\"a\":1.0,\"d\":\"test\"}"
};
for (String json : jsons) {
test(json);
}
}
private static void test(String json) {
BaseBezier objectFromJson = null;
try {
objectFromJson = mapper.readValue(json, BaseBezier.class);
ObjectMapper mapper = new ObjectMapper();
String jsonFromObject = mapper.writeValueAsString(objectFromJson);
System.out.println("Original:\t" + json);
System.out.println("Generated:\t" + jsonFromObject);
System.out.println("Is equal: " + json.equals(jsonFromObject));
} catch (JsonProcessingException e) {
System.err.println("Json exception: " + e.getMessage());
}
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "a")
@JsonSubTypes({
@JsonSubTypes.Type(names = {"0", "0.0"}, value = FixedBezier.class),
@JsonSubTypes.Type(names = {"1", "1.0"}, value = AnimatedBezier.class)
})
interface BaseBezier {
}
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
record FixedBezier(
@JsonProperty("a")
@JsonSerialize(using = AnimatedSerializer.class)
Integer animated,
@JsonProperty("d")
String data
) implements BaseBezier {
}
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public record AnimatedBezier(
@JsonProperty("a")
// @JsonSerialize(using = AnimatedSerializer.class)
Integer animated,
@JsonProperty("d")
String data
) implements BaseBezier {
}
private static class AnimatedSerializer extends JsonSerializer {
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeNumber((int) o);
}
}
}
Upvotes: 1
Views: 777
Reputation: 9408
The problem is that you're using JSON field "a" as both a type identifier:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "a")
and as a field identifier:
@JsonProperty("a")
Since the type identifiers have to be string values, this is what you're seeing in the resultant JSON - the type id expressed as a string.
If you change one of them to use a different field name, and update your test JSON accordingly, then it works, e.g.
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "id")
// jsonLottieFilesFixed
"""
{ "id": "0", "a": 0, "d": "test"}
""",
with the following output:
Original:
{ "id": "0", "a": 0, "d": "test"}
Generated:
{"id":"0","a":0,"d":"test"}
Upvotes: 2