【问题标题】:Custom Jackson Deserialization of a Generic Abstract class通用抽象类的自定义 Jackson 反序列化
【发布时间】:2018-03-06 21:34:40
【问题描述】:

我在尝试反序列化以下类时遇到问题:

public class MetricValuesDto {

    private Map<MetricType, MetricValueDto<?>> metricValues;

    public MetricValuesDto() {
    }

    public MetricValuesDto(Map<MetricType, MetricValueDto<?>> metricValues) {
        this.metricValues = metricValues;
    }

    public Map<MetricType, MetricValueDto<?>> getMetricValues() {
        return metricValues;
    }

    public void setMetricValues(Map<MetricType, MetricValueDto<?>> metricValues) {
        this.metricValues = metricValues;
    }
}

我的通用抽象类:

public abstract class MetricValueDto<T> {

    private T value;
    private MetricTrend trend;

    public MetricValueDto(T value, MetricTrend trend) {
        this.value = value;
        this.trend = trend;
    }

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }

    public MetricTrend getTrend() {
        return trend;
    }

    public void setTrend(MetricTrend trend) {
        this.trend = trend;
    }
}

我有两个实现MetricValueDto的具体类:

IntMetricValueDto:

public class IntMetricValueDto extends MetricValueDto<Integer> {

    public IntMetricValueDto(Integer value, MetricTrend trend) {
        super(value, trend);
    }
}

FloatMetricValueDto:

public class FloatMetricValueDto extends MetricValueDto<Float> {

    public FloatMetricValueDto(Float value, MetricTrend trend) {
        super(value, trend);
    }
}

知道什么是反序列化 MetricValueDto 的正确策略,以便我可以通过 ObjectMapperRestTemplate 解析它吗?每当我跑步时:

restTemplate.exchange("myEndpoint", HttpMethod.GET, entity, DataCollectionEventDto.class);

我明白了

原因:com.fasterxml.jackson.databind.JsonMappingException:无法构造com.resson.dto.MetricValueDto的实例:抽象类型要么需要映射到具体类型,要么具有自定义反序列化器,要么包含额外的类型信息

DataCollectionEventDto:

public class DataCollectionEventDto {

    private List<MapLayerDto> mapLayers;

    @JsonUnwrapped
    private MetricValuesDto metricValues;

    public List<MapLayerDto> getMapLayers() {
        return mapLayers;
    }

    public void setMapLayers(List<MapLayerDto> mapLayers) {
        this.mapLayers = mapLayers;
    }

    public MetricValuesDto getMetricValues() {
        return metricValues;
    }

    public void setMetricValues(MetricValuesDto metricValues) {
        this.metricValues = metricValues;
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }
}

我基本上已经尝试了网络上的所有内容,但我无法使其工作;任何建议都会有所帮助。

【问题讨论】:

标签: java spring serialization jackson type-erasure


【解决方案1】:

使用 JsonSubTypes 注释和 JsonTypeInfo 来指示子类型。 property 属性 JsonTypeInfo 用于区分不同的子类。

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "typ")
@JsonSubTypes({
        @JsonSubTypes.Type(value = IntMetricValueDto.class, name = "INT"),
        @JsonSubTypes.Type(value = FloatMetricValueDto.class, name = "FLT")}
public abstract class MetricValueDto<T> {

    private T value;
    private MetricTrend trend;
    ...
}

【讨论】:

  • 这是正确的做法。请记住,我需要添加一个虚拟构造函数public void IntMetricValueDto(){} 才能使其工作:)
  • 是的,您需要一个默认构造函数。谢谢!
  • @IndraBasak 非常感谢您的回答。这正是我长期以来一直坚持的问题。
  • @IndraBasak 我遇到了同样的问题,这个解决方案对我有用,但就我而言,我有 50 多个子类型。将来有机会添加更多内容,以便我们可以自动化此子类型吗?
【解决方案2】:

虽然JsonTypeInfo 有效,但会在响应中添加特定于实现的详细信息,这可能会在稍后给 API 客户端带来混乱。

我最终实现了自定义StdDeserializer

public class MetricValueDtoDeserializer<T> extends StdDeserializer<MetricValueDto<T>> {

    private static final long serialVersionUID = 1L;

    public MetricValueDtoDeserializer() {
        this(null);
    }

    public MetricValueDtoDeserializer(Class<?> vc) {
        super(vc);
    }

    private ObjectMapper mapper;

    @Override
    public MetricValueDto<T> deserialize(JsonParser jsonParser, DeserializationContext context)
            throws IOException, JsonProcessingException {
        String metricType = jsonParser.getCurrentName();
        mapper = (ObjectMapper) jsonParser.getCodec();
        ObjectNode objectNode = (ObjectNode) mapper.readTree(jsonParser);
        Iterator<Entry<String, JsonNode>> elementsIterator = objectNode.fields();
        Number number = null;
        while (elementsIterator.hasNext()) {
            Entry<String, JsonNode> element = elementsIterator.next();
            String key = element.getKey();
            if (key.equals("value")) {
                number = parseValue(element, metricType);
            }
            if (key.equals("trend")) {
                MetricTrend metricTrend = parseTrend(element);
                return (produceMetricValueDto(number, metricTrend));
            }
        }
        throw new IOException();
    }

    @SuppressWarnings("unchecked")
    private MetricValueDto<T> produceMetricValueDto(Number number, MetricTrend metricTrend) throws IOException {
        if (number instanceof Integer) {
            return (MetricValueDto<T>) new IntMetricValueDto((Integer) number, metricTrend);
        } else if (number instanceof Float) {
            return (MetricValueDto<T>) new FloatMetricValueDto((Float) number, metricTrend);
        }
        throw new IOException();
    }

    private MetricTrend parseTrend(Entry<String, JsonNode> element)
            throws JsonProcessingException {
        String trend = mapper.treeToValue(element.getValue(), String.class);
        if (trend == null) {
            return null;
        } else {
            return MetricTrend.valueOf(trend);
        }
    }

    private Number parseValue(Entry<String, JsonNode> element, String metricType)
            throws IOException {
        if (metricType.equals(MetricType.CANOPY_COVERAGE.toValue())
                || metricType.equals(MetricType.PLANT_SIZE.toValue())) {
            return mapper.treeToValue(element.getValue(), Float.class);
        } else if (metricType.equals(MetricType.INSECT_COUNT.toValue())
                || metricType.equals(MetricType.PLANT_COUNT.toValue())) {
            return mapper.treeToValue(element.getValue(), Integer.class);
        }
        throw new IOException();
    }
}

代码最终比 JsonTypeInfo 更复杂,但 API 客户端不知道特定于实现的细节。

【讨论】:

    猜你喜欢
    • 2017-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-10
    • 1970-01-01
    • 1970-01-01
    • 2014-08-02
    • 2016-01-16
    相关资源
    最近更新 更多