如何将单个 json 属性 反序列化为多个 Java 字段(如果可能的话使用转换器)

How to deserialize single json property into multiple Java fields (if possible with converter)

有这个class:

@Getter
@Setter
public class Result {

    private String positionText;
    private Integer positionNumber;
    .. many many other properties ..
}

并反序列化这个 json:

[
    {
        "position": "1",
        .. many many other properties ..
    },
    {
        "position": "FOO",
        .. many many other properties ..
    },
    ..
}

如何将 position json 属性 反序列化为 positionTextpositionNumber Java 字段?

public abstract class ResultMixIn {

    @JsonProperty("position")
    abstract String getPositionText();

    @JsonProperty("position")
    abstract Integer getPositionNumber();
}

但这给出了一个:

Conflicting getter definitions for property "position": com.example.domain.Result#getPositionText() vs com.example.domain.Result#getPositionNumber()

同样将抽象 getter 更改为 setter 也没有什么不同。

如果可能的话,我想避免完全成熟的 ResultDeserializer 扩展 StdDeserializer,因为 Result class 有更多我不想反序列化的属性”手工”。

PS:我不关心序列化。我只是反序列化模型。

首先需要注释Resultclass的属性, 这样 Jackson 将反序列化 positionText 属性, 但不是 positionNumber。 您将在 taylor-made 反序列化器中自己完成后者。

@Getter
@Setter
public class Result {

    @JsonProperty("position")
    private String positionText;
    
    @JsonIgnore
    private Integer positionNumber;
    
    .. many many other properties ..
}

默认情况下,Jackson 会使用 BeanDeserializer 来反序列化 Result 对象。 但是你想要这个反序列化器的实现稍微修改一下。

此答案的其余部分主要改编自已接受的答案 问题 How do I call the default deserializer from a custom deserializer in Jackson.

像往常一样,您的反序列化器从 StdDeserializer<Result> 扩展而来, 但它也实现了 ResolvableDeserializer 接口。 在 deserialize 方法中,大部分工作都委托给了默认的反序列化器 (在本例中是 BeanDeserializer),我们从 Jackson 那里得到。 我们只添加了一个小的额外逻辑来设置 positionNumber 属性 基于 positionText 属性.

public class ResultDeserializer extends StdDeserializer<Result> implements ResolvableDeserializer {

    private final JsonDeserializer<?> defaultDeserializer;

    public ResultDeserializer(JsonDeserializer<?> defaultDeserializer) {
        super(Result.class);
        this.defaultDeserializer = defaultDeserializer;
    }

    @Override
    public void resolve(DeserializationContext ctxt) throws JsonMappingException {
        if (defaultDeserializer instanceof ResolvableDeserializer) {
            // We need to resolve the default deserializer, or else it won't work properly.
            ((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
        }
    }

    @Override
    public Result deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        // let defaultDeserializer do the work:
        Result result = (Result) defaultDeserializer.deserialize(p, ctxt);

        // here you do your custom logic:
        String positionText = result.getPositionText();
        if (positionText != null) {
            try {
                result.setPositionNumber(Integer.valueOf(positionText));
            } catch(NumberFormatException e) {
                // positionText is not a valid integer
            }
        }

        return result;
    }
}

最后你需要告诉杰克逊你想要上面的东西ResultDeserializer 用于反序列化 Result 个对象。 这是通过 ObjectMapper 的以下自定义完成的, 这会将你的 ResultDeserializer 包裹在 Jackson 的周围 默认反序列化器,仅当要反序列化 Result 对象时:

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.registerModule(new SimpleModule()
            .setDeserializerModifier(new BeanDeserializerModifier() {
            
                @Override
                public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
                        BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
                    if (Result.class == beanDesc.getBeanClass())
                        return new ResultDeserializer(deserializer);  // your deserializer
                    return deserializer;
                }
            }));

然后您可以照常反序列化您的 JSON 内容,例如:

File file = new File("example.json");
List<Result> results = objectMapper.readValue(file, new TypeReference<List<Result>>() {});