使用Java在Jackson中进行反序列化

时间:2014-12-03 23:16:57

标签: java json generics jackson

我有以下课程

public class BetWrapper {

    private String description;
    private Calendar startTime;
    private HashMap<String, SimpleEntry<Integer, Double>> map;


    public BetWrapper() {
        map = new HashMap<>();
    }

    public Calendar getStartTime() {
        return startTime;
    }

    public void setStartTime(Calendar startTime) {
        this.startTime = startTime;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public HashMap<String, SimpleEntry<Integer, Double>> getMap() {
        return map;
    }

    public void setMap(HashMap<String, SimpleEntry<Integer, Double>> map) {
        this.map = map;
    }

}

我正在使用JSONUtil类

public class JSONUtil {

    private JSONUtil() {}

    public static <T> T fromJSON(String content, Class<T> clazz) throws TechnicalException {
        try {
            return new ObjectMapper().readValue(content, clazz);
        } catch (IOException e) {
            throw new TechnicalException(e);
        }
    }

    public static String toJSON(Object obj) throws TechnicalException {
        try {
            return new ObjectMapper().writeValueAsString(obj);
        } catch (JsonProcessingException ex) {
            throw new TechnicalException(ex);
        }
    }

}

我想将JSON反序列化为BetWrapper对象。但是下面的代码会产生一些例外。

BetWrapper betWrapper = new BetWrapper();
betWrapper.setDescription("Stoke City - Arsenal");
betWrapper.setStartTime(Calendar.getInstance());
HashMap<String, AbstractMap.SimpleEntry<Integer, Double>> map = new HashMap<>();
map.put("home_team", new AbstractMap.SimpleEntry<>(1, 2.85));
betWrapper.setMap(map);
String json = JSONUtil.toJSON(betWrapper);
JSONUtil.fromJSON(json, BetWrapper.class);

例外情况是:

Caused by: com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class java.util.AbstractMap$SimpleEntry<java.lang.Integer,java.lang.Double>]: can not instantiate from JSON object (need to add/enable type information?)
at [Source: {"description":"Stoke City - Arsenal","startTime":1417648132139,"map":{"home_team":"key":1,"value":2.85}}}; line: 1, column: 85] (through reference chain: by.bsu.kolodyuk.bettingapp.model.entity.BetWrapper["map"])

如何正确反序列化?似乎问题是SimpleEntry中的类型K,V应该以某种方式为Jackson指定。

任何想法?

2 个答案:

答案 0 :(得分:1)

类型SimpleEntry具有以下构造函数

public SimpleEntry(K key, V value) {
    this.key   = key;
    this.value = value;
}

默认情况下,Jackson需要一个无参数构造函数。如果这样的构造函数不存在,它会查找带有@JsonProperty注释的构造函数。 (我可能会倒退,但不要那么编码。)既然你正在使用JDK类型,它显然不会有这些注释。

你可以使用Mixins。 Jackson使用这些作为模板来反序列化您的目标类型。

abstract class Mixin<K, V> {
    public Mixin(@JsonProperty("key") K key, @JsonProperty("value") V value) {}        
} 
...
public static <T> T fromJSON(String content, Class<T> clazz) throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    mapper.addMixInAnnotations(SimpleEntry.class, Mixin.class);
    return mapper.readValue(content, clazz);
}

杰克逊将使用元类型Mixin来反序列化为SimpleEntry个对象。

(注意,Mixin的类型参数和构造函数参数类型并不重要。事实上有两个构造函数参数,构造函数参数是注释的。)

答案 1 :(得分:0)

在Jackson中,您可以定义自定义反序列化器。所以对于你的情况,它可能看起来像这样:

import java.io.IOException;
import java.util.AbstractMap;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;


public class SomeDeserializer extends StdDeserializer<AbstractMap.SimpleEntry>
{
    public SomeDeserializer()
    {
        super( AbstractMap.SimpleEntry.class );
    }

    @Override
    public AbstractMap.SimpleEntry deserialize( JsonParser jp, DeserializationContext ctxt ) throws IOException, JsonProcessingException
    {
        Integer key = null;
        Double value = null;

        JsonToken token;
        while ( ( token = jp.nextValue() ) != null )
        {
            if ( token.isNumeric() )
            {
                String propertyName = jp.getCurrentName();
                if ( "key".equalsIgnoreCase( propertyName ) )
                {
                    key = jp.getIntValue();
                }
                else if ( "value".equalsIgnoreCase( propertyName ) )
                {
                    value = jp.getDoubleValue();
                }
            }
        }

        if ( key != null && value != null )
        {
            return new AbstractMap.SimpleEntry( key, value );
        }
        return null;
    }
}

应使用ObjectMapper.registerModule(Module m)注册反序列化程序。在您的情况下,您可以在JSONUtil实用程序类中执行此操作:

SimpleModule module = new SimpleModule();
module.addDeserializer( AbstractMap.SimpleEntry.class, new SomeDeserializer() );

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule( module );

请注意,反序列化器已在ObjectMapper实例中注册。因此,您最好将实例存储为实用程序类的字段。

上面的反序列化程序类并不全面!这只是为了展示手头的情况。可以根据需要应用进一步的优化和重构。