2011-07-15 88 views
3

此JSON片段應映射到包含類型爲Map<String, Car>cars字段和類型爲Map<String, Bike>bikes字段的Java對象。由於自行車和汽車可以在JSON文件中爲空字符串,因此我需要一個自定義解串器(See this question)。ContextualDeserializer用於將JSON映射到具有Jackson的不同類型的映射

{ 
    "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<Car>JsonDeserializer<Map<String,?>>是不可能的。也許這在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; 
     } 
    } 

    // ... 
} 

回答

3

以下是我可能會接近它的方法。

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

泛型類型參數可能有點不合理。我做了一些快速複製粘貼。

+0

我怎麼能使用這樣一個解串器註釋? '@JsonDeserializer(?)public void setBikes(列表自行車)' – ipavlic

+0

我建議發佈一個新問題。 –

+0

對不起,我已經分別添加了我的問題:http://stackoverflow.com/questions/12933394/jackson-deserialize-as-an-empty-list/12944947#12944947,http://stackoverflow.com/questions/12937197 /如何到定義 - 一個泛型列表 - 解串器通的註解與 - 傑克遜 – ipavlic

相關問題