Java 如何使用Jackson和默认类型(反)序列化EnumMap?

Java 如何使用Jackson和默认类型(反)序列化EnumMap?,java,javascript,map,enums,jackson,Java,Javascript,Map,Enums,Jackson,我有一个对象,其中一个属性是映射 由于我的应用程序相当大,因此我启用了默认键入: ObjectMapper jsonMapper = new ObjectMapper() .enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_OBJECT) .configure(DeserializationConfig.Feature.FAIL_ON_UNKN

我有一个对象,其中一个属性是
映射

由于我的应用程序相当大,因此我启用了默认键入:

    ObjectMapper jsonMapper = new ObjectMapper()
        .enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_OBJECT)
        .configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
总的来说,这是相当好的

但是,当使用对象作为散列时,Javascript不支持对象键,当我从Javascript端将一些数据放入映射时,对象被转换为字符串

因此,我收到的JSON包含

     "MyClass": {
        "contextElements": {
          "userCredentials": {
            "UserCredentials": {
              "login": "admin",
              "password": "admin",
              }
            }
          }
        },
反序列化时,Jackson失败,出现以下异常

java.lang.IllegalArgumentException: Invalid type id 'userCredentials' (for id type 'Id.class'): no such class found
    at org.codehaus.jackson.map.jsontype.impl.ClassNameIdResolver.typeFromId(ClassNameIdResolver.java:72)
    at org.codehaus.jackson.map.jsontype.impl.TypeDeserializerBase._findDeserializer(TypeDeserializerBase.java:61)
    at org.codehaus.jackson.map.jsontype.impl.AsWrapperTypeDeserializer._deserialize(AsWrapperTypeDeserializer.java:87)
    at org.codehaus.jackson.map.jsontype.impl.AsWrapperTypeDeserializer.deserializeTypedFromObject(AsWrapperTypeDeserializer.java:39)
    at org.codehaus.jackson.map.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:133)
    at org.codehaus.jackson.map.deser.SettableBeanProperty$MethodProperty.deserializeAndSet(SettableBeanProperty.java:221)
我非常理解:Jackson不理解我的类中的
Map
声明,尽管
MyEnum
是最后一个类,但他希望添加一些类型元数据(嘿,可能是个bug?!)

我该怎么做才能让代码正常工作


我使用的是Jackson 1.5.2,所以问题正确地说明了这一点:不可能使用键不是字符串的JSON映射。因此,要在javascript中模拟Java映射,必须走更长的路,这通常涉及将映射转换为。。。还有别的

我选择的是非常常见的数组:

地图,如

{
    a:b,
    c:d,
}
然后将被转换为数组

[
    [a,b],
    [c,d],
]
获得该结果所需的详细步骤是什么

配置自定义(反)序列化 这是通过在对象映射器中设置序列化工厂获得的,如下所示:

这个过程看起来相当简单,主要是因为序列化在序列化中提供了非常正确的多态性特性,这对于反序列化来说并不太好。事实上,反序列化需要添加显式类映射,这些映射不以任何面向对象的方式使用(这里不支持继承)

是的,我将所有地图反序列化为bean。但是现在,我所有的反序列化程序都被正确调用了

连载 现在,序列化是一项相当简单的任务:

public class MapAsArraySerializer extends JsonSerializer<Map> {

    @SuppressWarnings("unchecked")
    private Set asListOfLists(Map<?, ?> value) {
        Set returned = new HashSet<>();
        for(Map.Entry e : value.entrySet()) {
            returned.add(Arrays.asList(e.getKey(), e.getValue()));
        }
        return returned;
    }

    @Override
    public void serialize(Map value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
        Collection entries = asListOfLists(value);
        jgen.writeObjectField("entries", entries);
    }

    @Override
    public void serializeWithType(Map value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException,
                    JsonProcessingException {
        Collection entries = asListOfLists(value);
        typeSer.writeTypePrefixForObject(value, jgen);
        jgen.writeObjectField("entries", entries);
        typeSer.writeTypeSuffixForObject(value, jgen);
    }
}
公共类MapAsArraySerializer扩展了JsonSerializer{
@抑制警告(“未选中”)
私有集AsListofList(映射值){
Set returned=newhashset();
对于(Map.Entry e:value.entrySet()){
returned.add(Arrays.asList(e.getKey(),e.getValue());
}
返回;
}
@凌驾
public void serialize(映射值、JsonGenerator jgen、SerializerProvider提供程序)引发IOException、JsonProcessingException{
集合条目=asListOfLists(值);
jgen.writeObject字段(“条目”,条目);
}
@凌驾
public void serializeWithType(映射值、JsonGenerator jgen、SerializerProvider提供程序、TypeSerializer typeSer)引发IOException,
JsonProcessingException{
集合条目=asListOfLists(值);
typeSer.writeTypePrefixForObject(值,jgen);
jgen.writeObject字段(“条目”,条目);
typeSer.writeTypeSuffixForObject(值,jgen);
}
}
反序列化 反序列化并不复杂:

public abstract class MapAsArrayDeserializer<Type extends Map> extends JsonDeserializer<Type> {

    protected Type newMap(Collection c, Type returned) {
        for(Object o : c) {
            if (o instanceof List) {
                List l = (List) o;
                if(l.size()==2) {
                    Iterator i = l.iterator();
                    returned.put(i.next(), i.next());
                }
            }
        }
        return returned;
    }

    protected abstract Type createNewMap() throws Exception;

    @Override
    public Type deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        if(jp.getCurrentToken().equals(JsonToken.START_OBJECT)) {
            JsonToken nameToken = jp.nextToken();
            String name = jp.getCurrentName();
            if(name.equals("entries")) {
                jp.nextToken();
                Collection entries = jp.readValueAs(Collection.class);
                JsonToken endMap = jp.nextToken();
                try {
                    return newMap(entries, createNewMap());
                } catch(Exception e) {
                    throw new IOException("unable to create receiver map", e);
                }
            } else {
                throw new IOException("expected \"entries\", but field name was \""+name+"\"");
            }
        } else {
            throw new IOException("not startying an object ? Not possible");
        }
    }

    @Override
    public Type deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException,
                    JsonProcessingException {
        Object value = typeDeserializer.deserializeTypedFromObject(jp, ctxt);
        return (Type) value;
    }
}
公共抽象类MapasArraydSerializer扩展JsonDeserializer{
受保护类型newMap(集合c,返回的类型){
用于(对象o:c){
if(o instanceof List){
列表l=(列表)o;
如果(l.size()==2){
迭代器i=l.迭代器();
returned.put(i.next(),i.next());
}
}
}
返回;
}
受保护的抽象类型createNewMap()引发异常;
@凌驾
公共类型反序列化(JsonParser jp,反序列化上下文ctxt)引发IOException,JsonProcessingException{
if(jp.getCurrentToken().equals(JsonToken.START_对象)){
JsonToken nameToken=jp.nextToken();
字符串名称=jp.getCurrentName();
如果(名称等于(“条目”)){
jp.nextToken();
Collection entries=jp.readValueAs(Collection.class);
JsonToken endMap=jp.nextToken();
试一试{
返回newMap(条目,createNewMap());
}捕获(例外e){
抛出新IOException(“无法创建接收器映射”,e);
}
}否则{
抛出新IOException(“应为\“条目\”,但字段名为\“+name+”\”);
}
}否则{
抛出新IOException(“不启动对象?不可能”);
}
}
@凌驾
公共类型deserializeWithType(JsonParser jp、DeserializationContext ctxt、TypeDeserializer TypeDeserializer)抛出IOException,
JsonProcessingException{
对象值=typeDeserializer.deserializeTypedFromObject(jp,ctxt);
返回(类型)值;
}
}
好的,类应该是左抽象的,而不是让每个声明的子类型创建右映射实例

现在呢
现在它可以在Java端无缝地工作(因为Javascript必须有一个映射等价的对象来读取这些数据。

这在这里可能无关紧要,但您确实应该升级到更高的Jackson版本。如果您想继续使用1.x,1.9(.13)是最新的——自1.5版本以来,许多错误已经修复。@StaxMan嗯,我已经考虑过了,但还没有遇到任何真正的Jackson错误。但是我可能会改变主意。。。
public class MapAsArrayDeserializerFactory extends CustomDeserializerFactory {
    @Override
    public JsonDeserializer<?> createMapDeserializer(DeserializationConfig config, MapType type, DeserializerProvider p) throws JsonMappingException {
        return createBeanDeserializer(config, type, p);
    }
}
public class MapAsArraySerializer extends JsonSerializer<Map> {

    @SuppressWarnings("unchecked")
    private Set asListOfLists(Map<?, ?> value) {
        Set returned = new HashSet<>();
        for(Map.Entry e : value.entrySet()) {
            returned.add(Arrays.asList(e.getKey(), e.getValue()));
        }
        return returned;
    }

    @Override
    public void serialize(Map value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
        Collection entries = asListOfLists(value);
        jgen.writeObjectField("entries", entries);
    }

    @Override
    public void serializeWithType(Map value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException,
                    JsonProcessingException {
        Collection entries = asListOfLists(value);
        typeSer.writeTypePrefixForObject(value, jgen);
        jgen.writeObjectField("entries", entries);
        typeSer.writeTypeSuffixForObject(value, jgen);
    }
}
public abstract class MapAsArrayDeserializer<Type extends Map> extends JsonDeserializer<Type> {

    protected Type newMap(Collection c, Type returned) {
        for(Object o : c) {
            if (o instanceof List) {
                List l = (List) o;
                if(l.size()==2) {
                    Iterator i = l.iterator();
                    returned.put(i.next(), i.next());
                }
            }
        }
        return returned;
    }

    protected abstract Type createNewMap() throws Exception;

    @Override
    public Type deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        if(jp.getCurrentToken().equals(JsonToken.START_OBJECT)) {
            JsonToken nameToken = jp.nextToken();
            String name = jp.getCurrentName();
            if(name.equals("entries")) {
                jp.nextToken();
                Collection entries = jp.readValueAs(Collection.class);
                JsonToken endMap = jp.nextToken();
                try {
                    return newMap(entries, createNewMap());
                } catch(Exception e) {
                    throw new IOException("unable to create receiver map", e);
                }
            } else {
                throw new IOException("expected \"entries\", but field name was \""+name+"\"");
            }
        } else {
            throw new IOException("not startying an object ? Not possible");
        }
    }

    @Override
    public Type deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException,
                    JsonProcessingException {
        Object value = typeDeserializer.deserializeTypedFromObject(jp, ctxt);
        return (Type) value;
    }
}