有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

通用抽象类的java自定义反序列化

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

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);

我明白了

Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.resson.dto.MetricValueDto: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information

DataCollectionEventTo:

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);
    }
}

我基本上尝试过网络上的所有东西,但都没能成功;任何建议都会有帮助


共 (2) 个答案

  1. # 1 楼答案

    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客户端不知道具体实现的细节

  2. # 2 楼答案

    使用JsonSubTypes注释和JsonTypeInfo来指示子类型。属性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;
        ...
    }