问题
Given a JSON object having a mutable property (e.g. label) which can be either primitive value (e.g. string) or an object. A hypothetical use-case could be a wrapper for pluralized translation of a label:
{
"label": "User name"
}
or
{
"label": {
"one": "A label",
"other": "The labels"
}
}
The goal is to bring Jackson deserialization always return a fixed structure on the Java-side. Thus, if a primitive value is given it is always translated to a certain property (e.g. other) of the target POJO, i.e.:
public class Translations {
@JsonDeserialize(using = PluralizedTranslationDeserializer.class)
public PluralizedTranslation label;
}
public class PluralizedTranslation {
public String one;
public String other; // used as default fields for primitive value
}
Currently the issue is solved by using a custom JsonDeserializer which checks whether the property is primitive or not:
public class PluralizedTranslationDeserializer extends JsonDeserializer {
@Override
public PluralizedTranslation deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);
PluralizedTranslation translation;
if (node.isTextual()) {
translation = new PluralizedTranslation();
translation.other = node.asText();
} else {
translation = oc.treeToValue(node, PluralizedTranslation.class);
}
return translation;
}
}
Is there a more elegant approach for handling mutable JSON properties without having a decoder which operates on node level?
回答1:
You could make the label
setter more generic and add some logic handling the two cases.
public class Translations {
// Fields omitted.
@JsonProperty("label")
public void setLabel(Object o) {
if (o instanceof String) {
// Handle the first case
} else if (o instanceof Map) {
// Handle the second case
} else {
throw new RuntimeException("Unsupported");
}
}
}
Alternative solution, which places the factory method inside the PluralizedTranslation
class, leaving the Translations
class unaffected:
public class PluralizedTranslation {
public String one;
public String other; // used as default fields for primitive value
@JsonCreator
private PluralizedTranslation(Object obj) {
if (obj instanceof Map) {
Map map = (Map) obj;
one = (String) map.get("one");
other = (String) map.get("other");
} else if (obj instanceof String) {
other = (String) obj;
} else {
throw new RuntimeException("Unsupported");
}
}
}
Note that the constructor can be marked as private
to prevent unintended usage.
来源:https://stackoverflow.com/questions/42205497/deserialize-mutable-primitive-object-json-property-to-object-with-jackson