首页 > 解决方案 > 如何将单个 json 属性反序列化为多个 Java 字段(如果可能使用转换器)

问题描述

有这个类:

@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 ..
    },
    ..
}

positionjson 属性如何反序列化为positionTextpositionNumberJava 字段?

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该类具有更多我不希望“手动”反序列化的属性。

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

标签: jsonjacksonjackson-databind

解决方案


首先,您需要注释Result类的属性,以便杰克逊将反序列化该positionText属性,而不是positionNumber. 您将在泰勒制造的反序列化器中自己完成后者。

@Getter
@Setter
public class Result {

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

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

这个答案的其余部分很大程度上是对问题的接受答案的改编,我如何从 Jackson 的自定义反序列化器中调用默认反序列化器

像往常一样,您的反序列化器从 扩展StdDeserializer<Result>,但它也实现了 ResolvableDeserializer接口。在该deserialize方法中,大部分工作都委托给BeanDeserializer我们从 Jackson 获得的默认反序列化器(在本例中为 a )。我们只添加了一个小的额外逻辑,用于根据属性设置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;
    }
}

最后,您需要告诉 Jackson 您希望将上述内容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>>() {});

推荐阅读