使用Jackson @JsonDeserializer将一个自定义Java字段映射到许多JSON字段

时间:2019-04-05 20:51:15

标签: java json jackson hl7-fhir

我有一个使用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开头的字段,并适当地设置value字段。效果很好!但是,这样做时,我现在必须在解串器中手动设置此MyPojo类中的所有其他字段,并且必须在每个使用{{1} }。

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*属性中(而该属性现在位于顶级资源上)。第一个缺陷是,除非JSON属性与value完全匹配,否则甚至不会调用反序列化器。我真正需要的是将整个父JSON资源传递到该特定于字段的解串器,以便我可以找到匹配的字段并为其分配-或-我需要能够在{{1 }}仅 分配一个字段value,并允许自动反序列化处理其他字段。我该怎么做?


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

1 个答案:

答案 0 :(得分:1)

我认为@JsonAnySetter是最适合您的问题。此方法注释告诉Jackson向其路由未知属性。 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;
}