ContextualDeserializer用于将JSON映射到与Jackson不同类型的地图 [英] ContextualDeserializer for mapping JSON to different types of maps with Jackson

查看:1086
本文介绍了ContextualDeserializer用于将JSON映射到与Jackson不同类型的地图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个JSON片段应该被映射到一个Java对象,该对象包含 Map< String,Car> cars $ c>和一个自行车类型的字段 Map< String,Bike> 。因为自行车和汽车可以在JSON文件中为空字符串,所以我需要一个自定义的反序列化器(

  {
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
}
},
自行车:{
Bike 1:{
color:black$ b $ b,
Bike 2:{
color:red
}
}
}
createContextual(DeserializationConfig cfg,BeanProperty属性)返回的泛型自定义反序列化器的实例,
基于BeanProperty参数的 ContextualDeserializer 的方法。通用自定义反序列化器看起来像这样:

  public class MapsGenericDeserializer< T>扩展
JsonDeserializer< Map< String,T>> {

私有ObjectMapper映射器; //无需特殊映射反序列化器的ObjectMapper

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

@Override
public Map< String,T> deserialize(JsonParser jp,DeserializationContext ctxt)
抛出IOException,JsonProcessingException {
ObjectCodec codec = jp.getCodec();
JsonNode node = codec.readTree(jp);
if(!。equals(node.getTextValue())){
return mapper.readValue(node,
new TypeReference< Map< String,T>>(){} );
}
返回null; // Node是一个空字符串
}
}

下面的上下文序列化程序从 MapsGenericDeserializer< Car> 转换为 JsonDeserializer< Map< String,?>> 不适用可能。也许这在Java的新版本中是可行的,但它不适用于我编码的Android版本。所以我怎么能实现所需的行为?

  public class MapsDeserializer扩展了JsonDeserializer< Map< String,?>> 
实现了ContextualDeserializer< Map< String,?>>> {

私有ObjectMapper映射器;

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

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

@Override
public JsonDeserializer< Map< String,?>> createContextual(DeserializationConfig cfg,
BeanProperty属性)抛出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; //类型不匹配!
} else {
return this;
}
}

// ...


解决方案

以下是我可能会遇到的问题。 / p>

  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.DeserializationCo ntext;
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 =
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 Map< String,Car>汽车;
public Map< String,Bike>自行车;
}

class Car
{
public String color;
public String buying_date;
}

class Bike
{
public String color;
}

class EmptyStringAsMapDeserializer< T>
扩展了JsonDeserializer< Map< String,?>>
实现了ContextualDeserializer< Map< String,?>>>
{
private Class<?> TARGETTYPE;
私人ObjectMapper映射器;

EmptyStringAsMapDeserializer(Class<?> targetType,ObjectMapper mapper)
{
this.targetType = targetType;
this.mapper = mapper;
}

@Override
public JsonDeserializer< Map< String,?>> createContextual(DeserializationConfig config,BeanProperty属性)
throws JsonMappingException
{
返回新EmptyStringAsMapDeserializer< Object>(property.getType()。containedType(1).getRawClass(),mapper);
}

@Override
public Map< String,?> deserialize(JsonParser jp,DeserializationContext ctxt)
抛出IOException异常,JsonProcessingException异常
{
JsonNode node = jp.readValueAsTree();
if(.equals(node.getTextValue()))
返回新的HashMap< String,Object>();
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(node,mapper.getTypeFactory()。constructMapType(Map.class,String.class,targetType));


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


This JSON snippet should be mapped to a Java-Objects that contains a cars field of type Map<String, Car> and a bikes field of type Map<String, Bike>. Because bikes and cars can be empty strings in the JSON file, i need a custom deserializer (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"
        }
    }
}

I thought about having instances of a generic custom deserializer that can be returned by the createContextual(DeserializationConfig cfg, BeanProperty property) method of a ContextualDeserializer based on the BeanProperty parameter. The generic custom deserializer looks like this:

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

The contextual serializer below does not work because casting from MapsGenericDeserializer<Car> to JsonDeserializer<Map<String,?>> is not possible. Maybe this is possible in newer Versions of Java, but it does not work on the Version of Android I am coding for. So how can I implement the desired behaviour?

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

    // ...
}

解决方案

Here's how I might approach it.

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

The generic type parameters might be a little out of order. I did a bit of quick copy-pasting.

这篇关于ContextualDeserializer用于将JSON映射到与Jackson不同类型的地图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆