有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

找不到java静态定义的KeyDeserializer,但如果在本地定义,则一切都是完美的

我对注册自定义KeyDeserializer的工作方式感到困惑

这是我的密码:

比赛日。爪哇

package com.example;

import java.io.Serializable;
import java.util.Objects;

public class Matchday implements Serializable, Comparable<Matchday> {
    private static final long serialVersionUID = -8823049187525703664L;

    private final int matchdayNumber;

    public Matchday(final int matchdayNumber) {
        this.matchdayNumber = matchdayNumber;
    }

    public int getMatchdayNumber() {
        return matchdayNumber;
    }

    @Override
    public int compareTo(Matchday o) {
        return Integer.compare(matchdayNumber, o.getMatchdayNumber());
    }

    @Override
    public final int hashCode() {
        return Objects.hash(matchdayNumber);
    }

    @Override
    public final boolean equals(final Object obj) {
        return obj instanceof Matchday && Integer.valueOf(matchdayNumber).equals(((Matchday) obj).matchdayNumber);
    }

    @Override
    public String toString() {
        return Integer.toString(matchdayNumber);
    }
}

团队精神。爪哇

package com.example;

import java.io.Serializable;

import org.apache.commons.lang3.builder.ToStringBuilder;

public class TeamPlayer implements Serializable {
    private static final long serialVersionUID = -6057852081020631549L;

    private int id;
    private String name;
    private String surname;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this).append("id", id).append("name", name).append("surname", surname).build()
                .toString();
    }
}

现在,如果我为我的类Matchday定义一个自定义映射键反序列化器。java,如果我这样做的话,它会很有魅力

键反序列化测试。爪哇

package com.example;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.SortedMap;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;

public class KeyDeserializerTest {

    public static void main(String[] args) throws IOException {
        final ObjectMapper objectMapper = new ObjectMapper();
        final SimpleModule mySimpleModule = new SimpleModule("dummy", new Version(0, 0, 0, "dummy", "dummy", "dummy"));
        mySimpleModule.addKeyDeserializer(Matchday.class, new KeyDeserializer() {

            @Override
            public Object deserializeKey(String arg0, DeserializationContext arg1)
                    throws IOException, JsonProcessingException {
                return new Matchday(Integer.valueOf(arg0));
            }
        });
        objectMapper.registerModule(mySimpleModule);

        final InputStream inputStream = new ByteArrayInputStream(
                "{\"1\":[{\"id\": 1, \"name\": \"Arkadiusz\", \"surname\": \"Malarz\"}]}".getBytes());
        SortedMap<Matchday, List<TeamPlayer>> map = objectMapper.readValue(inputStream,
                new TypeReference<SortedMap<Matchday, List<TeamPlayer>>>() {
                });
        System.out.println(map);
    }

}

它打印

{1=[com.example.TeamPlayer@3a8624[id=1,name=Arkadiusz,surname=Malarz]]}

但是,如果我将对象映射器和反序列化器实例都定义为静态属性,则会出现以下异常

KeyDeserializerStaticTest。爪哇

package com.example;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.SortedMap;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;

public class KeyDeserializerStaticTest {
    public static final ObjectMapper OBJECT_MAPPER = createObjectMapper();

    private static final KeyDeserializer MATCHDAY_KEY_DESERIALIZER = new KeyDeserializer() {

        @Override
        public Object deserializeKey(String key, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            return new Matchday(Integer.valueOf(key));
        }
    };

    private static ObjectMapper createObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(createSimpleModule());
        return objectMapper;
    }

    private static Module createSimpleModule() {
        SimpleModule simpleModule = new SimpleModule("dummy", new Version(0, 0, 0, "dummy", "dummy", "dummy"));
        simpleModule.addKeyDeserializer(Matchday.class, MATCHDAY_KEY_DESERIALIZER);
        return simpleModule;
    }

    public static void main(String[] args) throws IOException {
        final InputStream inputStream = new ByteArrayInputStream(
                "{\"1\":[{\"id\": 1, \"name\": \"Arkadiusz\", \"surname\": \"Malarz\"}]}".getBytes());
        SortedMap<Matchday, List<TeamPlayer>> map = OBJECT_MAPPER.readValue(inputStream,
                new TypeReference<SortedMap<Matchday, List<TeamPlayer>>>() {
                });
        System.out.println(map);
    }
}
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not find a (Map) Key deserializer for type [simple type, class com.example.Matchday]
 at [Source: java.io.ByteArrayInputStream@bbc1e0; line: 1, column: 1]
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:270)
    at com.fasterxml.jackson.databind.DeserializationContext.reportMappingException(DeserializationContext.java:1234)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._handleUnknownKeyDeserializer(DeserializerCache.java:585)
    at com.fasterxml.jackson.databind.deser.DeserializerCache.findKeyDeserializer(DeserializerCache.java:168)
    at com.fasterxml.jackson.databind.DeserializationContext.findKeyDeserializer(DeserializationContext.java:499)
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer.createContextual(MapDeserializer.java:247)
    at com.fasterxml.jackson.databind.DeserializationContext.handleSecondaryContextualization(DeserializationContext.java:681)
    at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:481)
    at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:3899)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3794)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2915)
    at com.example.KeyDeserializerStaticTest.main(KeyDeserializerStaticTest.java:43)

这里怎么了?在语义上,上述主要方法没有区别。这是一个有文档记录的功能,还是仅仅是Jackson中的一个bug


共 (1) 个答案

  1. # 1 楼答案

    这里的根本问题是静态变量的初始化顺序

    是的

    public static final ObjectMapper OBJECT_MAPPER = createObjectMapper();
    
    private static final KeyDeserializer MATCHDAY_KEY_DESERIALIZER = new KeyDeserializer() {
    
        @Override
        public Object deserializeKey(String key, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            return new Matchday(Integer.valueOf(key));
        }
    };
    

    虽然应该如此

    private static final KeyDeserializer MATCHDAY_KEY_DESERIALIZER = new KeyDeserializer() {
    
        @Override
        public Object deserializeKey(String key, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            return new Matchday(Integer.valueOf(key));
        }
    };
    
    public static final ObjectMapper OBJECT_MAPPER = createObjectMapper();
    

    这很难发现,因为类SimpleModule的方法addKeyDeserializer(Class<?>, KeyDeserializer)会无声地向内部键反序列化器映射添加null引用。在我看来,它应该在尝试添加NullPointerException键反序列化器引用时抛出null

    杰克逊的密码是这样的

    第一addKeKeyDeserializer(Class<?>, KeyDeserializer)

    public SimpleModule addKeyDeserializer(Class<?> type, KeyDeserializer deser)
    {
        if (_keyDeserializers == null) {
            _keyDeserializers = new SimpleKeyDeserializers();
        }
        _keyDeserializers.addDeserializer(type, deser);
        return this;
    }
    

    这里没有检查deser是否为null

    然后它将委托给类SimpleKeyDeserializers的addDeserializer(类,KeyDeserializer)

    public SimpleKeyDeserializers addDeserializer(Class<?> forClass, KeyDeserializer deser)
    {
        if (_classMappings == null) {
            _classMappings = new HashMap<ClassKey,KeyDeserializer>();
        }
        _classMappings.put(new ClassKey(forClass), deser);
        return this;
    }
    

    这里是null引用,它也被忽略并静默地放入_classMappings映射中

    Here是我在GitHub上发布的问题和讨论