当我用泛型(用Jackson)将JSON反序列化到我的Java类中时,由于递归,我遇到了堆栈溢出,我该怎么解决这个问题?

huwehgph  于 2023-06-20  发布在  Java
关注(0)|答案(1)|浏览(181)

我正在使用Jackson将JSON反序列化为Java。我有以下类(只显示片段):

public class NavMenu {
    private final List<Menu> menus = new ArrayList<>();
}

public class Menu implements ItemContainer<Menu> {
    private final List<Item<Menu>> items = new ArrayList<>();
}

public class Item<P extends ItemContainer<P>> implements ItemContainer<Item<P>> {
    private final List<Item<Item<P>>> items = new ArrayList<>();
}

public interface ItemContainer<T extends ItemContainer<T>> {

    void add(Item<T> item);

    List<Item<T>> getItems();
}

看来,在反序列化Menu时,会出现递归,最终导致StackOverflow错误。stacktrace看起来如下:

java.lang.StackOverflowError
    at com.fasterxml.jackson.databind.type.TypeBase.toCanonical(TypeBase.java:72)
    at com.fasterxml.jackson.databind.type.SimpleType.buildCanonicalName(SimpleType.java:236)
    ...

我实际上通过编写我自己的NavMenu解串器解决了这个问题,但它是递归的,相当难以理解,所以我想知道这是否是Jackson中的一个bug,或者是否有注解/方法来避免必须编写我自己的解串器。

ifsvaxew

ifsvaxew1#

您遇到的问题不是Jackson中的错误,而是它在处理递归数据结构方面的限制。Jackson的反序列化默认行为是使用反射并递归遍历对象图,这在处理深度嵌套结构时可能导致堆栈溢出错误。
Jackson允许您注册可以修改其默认行为的模块。可以创建自定义模块并重写某些类型的默认处理。这种方法提供了更大的灵活性,并允许您封装自定义的反序列化逻辑
例如-

import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import java.io.IOException;

public class NavMenuDeserializer extends StdDeserializer<NavMenu> {

    public NavMenuDeserializer() {
        this(null);
    }

    public NavMenuDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public NavMenu deserialize(JsonNode node, DeserializationContext context) throws IOException {
        NavMenu navMenu = new NavMenu();
        JsonNode menusNode = node.get("menus");
        if (menusNode != null) {
            for (JsonNode menuNode : menusNode) {
                Menu menu = context.readValue(menuNode.traverse(), Menu.class);
                navMenu.add(menu);
            }
        }
        return navMenu;
   }
}

要使用自定义反序列化器,您可以像这样将其注册到ObjectMapper示例:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;

public class Main {
    public static void main(String[] args) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addDeserializer(NavMenu.class, new NavMenuDeserializer());
        objectMapper.registerModule(module);

        // Now you can use objectMapper to deserialize your JSON
        NavMenu navMenu = objectMapper.readValue(jsonString, NavMenu.class);
    }
}

通过提供自定义的反序列化程序,您可以对反序列化过程进行更多的控制,并且可以根据需要处理递归和复杂的对象关系

相关问题