【问题标题】:Mapping one custom Java field to many JSON fields using Jackson @JsonDeserializer使用 Jackson @JsonDeserializer 将一个自定义 Java 字段映射到多个 JSON 字段
【发布时间】:2019-04-17 17:52:36
【问题描述】:

我有一个使用 Jackson 表示 JSON 的 java 类。除了一个例外,所有字段都可以完全不使用注释进行翻译。一对一的简单翻译(尽管其中一些是嵌套的 POJO)。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyPojo {
    private String someString;
    private AnotherPojo someOtherPojo;
    //The problem child:
    private Object value;
}

字段value 是此规则的一个例外,它可以表示任何匹配value* 的JSON 字段,其中* 是不定长度的通配符。这意味着 JSON 中的 valueStringvalueReference 将被分配给该字段,并断言可能只有一个存在。

{
  "someString": "asdasdadasdsa",
  "someOtherPojo": {
    "someOtherProperty": "whatever"
  },
  "valueCodeableConcept": {
    "text": "text value",
    "coding": [
      {
        "code": "my-code"
      }
    ]
  }
}

使用顶级类上的自定义反序列化器,我可以从根节点(以下示例中为baseNode)中抓取所有以value 开头的字段,并适当地设置值字段。效果很好!但是,在这样做的过程中,我现在必须在我的反序列化器中手动设置此 MyPojo 类中的所有其他字段,并且我必须在每个使用类似 value* 的字段的 POJO 上放置此反序列化器的自定义副本。

private Object parseValueX(JsonNode baseNode, DeserializationContext context) throws IOException {
    //Find the concrete implementation referred to by the value[x] field
    Set<String> concreteNames = new HashSet<>();
    baseNode.fieldNames().forEachRemaining(name -> {
        if (name.startsWith("value")) {
            concreteNames.add(name);
        }});

    if (concreteNames.isEmpty()) {
        return null;
    }

    if (concreteNames.size() > 1) {
        throw JsonMappingException.from(context, "The field value[x] must have no more than one concrete " +
                "implementation, ex: valueCode, valueCodeableConcept, valueReference");
    }

    String concreteName = concreteNames.stream().findFirst().orElseThrow(() -> new RuntimeException(""));
    JsonNode jsonSource = baseNode.get(concreteName);

    //...deserialize from jsonSource, solved, but not relevant to question...
}

为了使它适用于任何 POJO 上的任何 value* 属性,我尝试将反序列化器移动到 POJO 中的 value 属性(而它现在位于顶级资源中)。第一个缺陷是反序列化器甚至不会被调用,除非 JSON 属性与 value 完全匹配。我真正需要的是将整个父 JSON 资源传递给特定于字段的反序列化器,以便我可以找到匹配的字段并分配它——或者——我需要能够在 MyPojo 上拥有反序列化器分配一个字段value 并允许自动反序列化处理其他字段。我该怎么做?


对于那些对我的动机感到好奇的人,我正在实施 HL7 FHIR 规范,它指定了称为 value[x] 的通用属性(这里有一个示例:https://www.hl7.org/fhir/extensibility.html#Extension),其中 [x] 成为资源的类型。

【问题讨论】:

    标签: java json jackson hl7-fhir


    【解决方案1】:

    我认为适合你的问题是@JsonAnySetter。此方法注释告诉杰克逊将未知属性路由到它。 arg(在您的情况下)是一个包含未知属性的 json 树的 Map。如果我正确理解您的代码,则 value 属性的名称包含目标 Pojo 的类名。因此,一旦有了类名,就可以告诉 Jackson 如何将映射“反序列化”为目标类的实例。

    这是一个基于问题代码的示例

    public class MyPojo {
        public String someString;  // made properties into public for this example...
        public AnotherPojo someOtherPojo;
        public Object value;
    
        @JsonAnySetter
        public void setValue(String name, Object value) {
            System.out.println(name + " " + value.getClass());
            System.out.println(value);
    
            // basic validation
            if (name.startsWith("value") && value instanceof Map) {
                String className = "com.company." + name.substring("value".length());
                System.out.println(name + " " + value.getClass() + " " + className);
                System.out.println(value);
                try {
                    // nice of Jackson to be able to deserialize Map into Pojo :)
                    ObjectMapper mapper = new ObjectMapper();
                    this.value = mapper.convertValue(value, Class.forName(className));
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(this.value + " " + this.value.getClass());
            }
    
        }
    }
    
    public class AnotherPojo {
        public String someOtherProperty;
    }
    
    public class CodeableConcept {
        public String text;
        public Code[] coding;
    }
    
    public class Code {
        public String code;
    }
    

    【讨论】:

    • 太棒了,相反的注释@JsonAnyGetter 让我可以轻松地序列化这个资源。我今天将实施这一点,如果它按预期工作,我会接受你的回答。
    猜你喜欢
    • 2020-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多