Java 使用Jackson将可变(原语/对象)JSON属性反序列化为对象
给定一个JSON对象,该对象具有可变属性(例如标签),可以是原语值(例如字符串)或对象。假设的用例可以是标签多重翻译的包装器:Java 使用Jackson将可变(原语/对象)JSON属性反序列化为对象,java,json,jackson,deserialization,Java,Json,Jackson,Deserialization,给定一个JSON对象,该对象具有可变属性(例如标签),可以是原语值(例如字符串)或对象。假设的用例可以是标签多重翻译的包装器: { "label": "User name" } 或 目标是使反序列化始终在Java端返回一个固定的结构。因此,如果给定一个原语值,它总是转换为目标POJO的某个属性(例如,其他),即: public class Translations { @JsonDeserialize(using = PluralizedTranslationDeserialize
{
"label": "User name"
}
或
目标是使反序列化始终在Java端返回一个固定的结构。因此,如果给定一个原语值,它总是转换为目标POJO的某个属性(例如,其他),即:
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
}
目前,通过使用检查属性是否为原始属性的自定义项来解决此问题:
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;
}
}
有没有一种更优雅的方法来处理可变的JSON属性,而不需要在节点级运行的解码器?您可以使
标签设置器更通用,并添加一些处理这两种情况的逻辑
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");
}
}
}
替代解决方案,它将工厂方法放在多重翻译
类中,使翻译
类不受影响:
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");
}
}
}
请注意,构造函数可以标记为private
,以防止意外使用。这个问题与可变性无关。您只是有一个属性,并希望以某种方式对其进行反序列化。@Miha_x64我将属性值描述为可变的,因为其值的类型是可变的。很抱歉让人误解这个形容词,这个问题当然不是关于(Java)对象的可变性。谢谢你的回答。实际上,此解决方案消除了JsonDeserializer
。不幸的是,它还将pluralizedtransation
创建逻辑移到了POJO中。因此,它将获得对某些工厂和/或ObjectMapper
的依赖关系,而这些工厂和/或对象映射器实际上并不漂亮。此外,对于私有
字段(为了简单起见,在问题中省略了这些字段),我们需要为添加额外的检查,以进行实例多重翻译
。总的来说,这个替代方案在我看来并不干净……@dim:我不能完全理解你的反对意见,但我会尽力回答。在任何情况下都需要某种工厂。我认为,把它放在课堂上会更容易理解发生了什么。我不明白需要一个多重翻译的o实例。在这两种情况下,都会创建一个多重翻译的实例,因此这将是该类用户唯一需要处理的事情。我正在添加我提出的另一种方法,它将多重翻译的创建放在一个私有构造函数中,我觉得这相当优雅。我同意你的解决方案是可以理解的。我的第一个反对意见与这样一个事实有关,即setLabel
应该能够接受pluralizedtransation
的有效实例(这目前会导致运行时异常
),但这是一个很简单的问题。第二:使用ObjectMapper
而不是手工制作的映射是合理的,这意味着必须将其注入POJO。这是我不舒服的地方。然而,您的建议对于给定的简单情况是有效的,但我更多的是搜索magic@JsonDefaultValue
annotation;)。
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");
}
}
}