【问题标题】:ContextualDeserializer for mapping JSON to different types of maps with JacksonContextualDeserializer 用于使用 Jackson 将 JSON 映射到不同类型的地图
【发布时间】:2011-10-06 00:11:07
【问题描述】:

此 JSON sn-p 应映射到包含 Map<String, Car> 类型的 cars 字段和 Map<String, Bike> 类型的 bikes 字段的 Java 对象。因为自行车和汽车在 JSON 文件中可以是空字符串,所以我需要一个自定义反序列化器 (See this question)。

{
    "id" : "1234",
    "name" : "John Doe",
    "cars" : {
        "Tesla Model S" : {
            "color" : "silver",
            "buying_date" : "2012-06-01"
        },
        "Toyota Yaris" : {
            "color" : "blue",
            "buying_date" : "2005-01-01"
        }
    },
    "bikes" : {
        "Bike 1" : {
            "color" : "black"
        },
        "Bike 2" : {
            "color" : "red"
        }
    }
}

我想过拥有一个通用自定义反序列化程序的实例,该实例可以由基于 BeanProperty 参数的ContextualDeserializercreateContextual(DeserializationConfig cfg, BeanProperty property) 方法返回。通用自定义反序列化器如下所示:

public class MapsGenericDeserializer<T> extends
        JsonDeserializer<Map<String, T>> {

    private ObjectMapper mapper; // ObjectMapper without special map deserializer

    public MapsGenericDeserializer(ObjectMapper mapper) {
        this.mapper = mapper;
    }

    @Override
    public Map<String, T> deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        ObjectCodec codec = jp.getCodec();
        JsonNode node = codec.readTree(jp);
        if (!"".equals(node.getTextValue())) {
            return mapper.readValue(node, 
                    new TypeReference<Map<String, T>>() {});
        }
        return null; // Node was an empty string
    }
}

下面的上下文序列化程序不起作用,因为无法从MapsGenericDeserializer&lt;Car&gt; 转换为JsonDeserializer&lt;Map&lt;String,?&gt;&gt;。也许这在较新版本的 Java 中是可能的,但它不适用于我正在编码的 Android 版本。那么如何实现所需的行为呢?

public class MapsDeserializer extends JsonDeserializer<Map<String, ?>>
        implements ContextualDeserializer<Map<String, ?>> {

    private ObjectMapper mapper;

    MapsGenericDeserializer<Car> carDeserializer = new MapsGenericDeserializer<Car>(mapper);
    MapsGenericDeserializer<Bike> bikeDeserializer = new MapsGenericDeserializer<Bike>(mapper);

    public MapsDeserializer(ObjectMapper mapper) {
        this.mapper = mapper;
    }

    @Override
    public JsonDeserializer<Map<String, ?>> createContextual(DeserializationConfig cfg,
            BeanProperty property) throws JsonMappingException {

        Class<?> targetClass = property.getType().containedType(1).getRawClass();

        if(targetClass.equals(Car.class) { 
            return carDeserializer; // Type mismatch!
        } else if (targetClass.equals(Bike.class)) {
            return bikeDeserializer; // Type mismatch!
        } else {
            return this;
        }
    }

    // ...
}

【问题讨论】:

    标签: json generics mapping jackson


    【解决方案1】:

    这是我可能的处理方法。

    import java.io.File;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    
    import org.codehaus.jackson.JsonNode;
    import org.codehaus.jackson.JsonParser;
    import org.codehaus.jackson.JsonProcessingException;
    import org.codehaus.jackson.Version;
    import org.codehaus.jackson.map.BeanProperty;
    import org.codehaus.jackson.map.ContextualDeserializer;
    import org.codehaus.jackson.map.DeserializationConfig;
    import org.codehaus.jackson.map.DeserializationContext;
    import org.codehaus.jackson.map.JsonDeserializer;
    import org.codehaus.jackson.map.JsonMappingException;
    import org.codehaus.jackson.map.ObjectMapper;
    import org.codehaus.jackson.map.module.SimpleModule;
    
    public class Foo
    {
      public static void main(String[] args) throws Exception
      {
        EmptyStringAsMapDeserializer<Map<String, ?>> emptyStringAsMapDeserializer = 
            new EmptyStringAsMapDeserializer<Map<String, ?>>(null, new ObjectMapper());
    
        SimpleModule module = new SimpleModule("ThingsDeserializer", Version.unknownVersion());
        module.addDeserializer(Map.class, emptyStringAsMapDeserializer);
    
        ObjectMapper mapper = new ObjectMapper().withModule(module);
    
        Person person = mapper.readValue(new File("input.json"), Person.class);
        System.out.println(mapper.writeValueAsString(person));
      }
    }
    
    class Person
    {
      public int id;
      public String name;
      public Map<String, Car> cars;
      public Map<String, Bike> bikes;
    }
    
    class Car
    {
      public String color;
      public String buying_date;
    }
    
    class Bike
    {
      public String color;
    }
    
    class EmptyStringAsMapDeserializer<T>
        extends JsonDeserializer<Map<String, ?>>
        implements ContextualDeserializer<Map<String, ?>>
    {
      private Class<?> targetType;
      private ObjectMapper mapper;
    
      EmptyStringAsMapDeserializer(Class<?> targetType, ObjectMapper mapper)
      {
        this.targetType = targetType;
        this.mapper = mapper;
      }
    
      @Override
      public JsonDeserializer<Map<String, ?>> createContextual(DeserializationConfig config, BeanProperty property)
          throws JsonMappingException
      {
        return new EmptyStringAsMapDeserializer<Object>(property.getType().containedType(1).getRawClass(), mapper);
      }
    
      @Override
      public Map<String, ?> deserialize(JsonParser jp, DeserializationContext ctxt)
          throws IOException, JsonProcessingException
      {
        JsonNode node = jp.readValueAsTree();
        if ("".equals(node.getTextValue()))
          return new HashMap<String, Object>();
        ObjectMapper mapper = new ObjectMapper();
        return mapper.readValue(node, mapper.getTypeFactory().constructMapType(Map.class, String.class, targetType));
      }
    }
    

    泛型类型参数可能有点乱。我做了一些快速的复制粘贴。

    【讨论】:

    猜你喜欢
    • 2023-03-30
    • 1970-01-01
    • 2015-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多