Почему я получаю ошибку stackoverflow при использовании jackson, даже если использую @JsonIgnoreProperties

#java #jackson #stack-overflow #mixins #self-reference

#java #jackson #переполнение стека #смешивания #ссылка на себя

Вопрос:

Я пытаюсь сериализовать объект DefaultMutableTreeNode с помощью jackson в строку json. Поэтому мне нужно использовать абстрактный класс mix-in, который является своего рода прокси для класса DefaultMutableTreeNode по умолчанию. Вероятно, это связано с полями самоссылки, но я не могу их распознать.

Смешанный класс:

 @JsonIgnoreProperties(ignoreUnknown = true)
public abstract class DefaultMutableTreeNodeMixIn {

    @JsonCreator
    public DefaultMutableTreeNodeMixIn(@JsonProperty Object userObject) {};

    @JsonCreator
    public DefaultMutableTreeNodeMixIn(@JsonProperty Object userObject, 
    @JsonProperty boolean allowsChildren) {};

    @JsonProperty("childCount")
    abstract int getChildCount();

    @JsonProperty("depth")
    abstract int getDepth();

    @JsonProperty("firstChild")
    abstract TreeNode getFirstChild();

    @JsonProperty("firstLeaf")
    abstract DefaultMutableTreeNode getFirstLeaf();

    @JsonProperty("lastChild")
    abstract TreeNode getLastChild();

    @JsonProperty("lastLeaf")
    abstract DefaultMutableTreeNode getLastLeaf();

    @JsonProperty("leafCount")
    abstract int getLeafCount();

    @JsonProperty("level")
    abstract int getLevel();

    @JsonProperty("nextLeaf")
    abstract DefaultMutableTreeNode getNextLeaf();

    @JsonProperty("nextNode")
    abstract DefaultMutableTreeNode getNextNode();

    @JsonProperty("nextSibling")
    abstract DefaultMutableTreeNode getNextSibling();

    @JsonProperty("parent")
    abstract TreeNode getParent();

    @JsonProperty("path")
    abstract TreeNode[] getPath();

    @JsonProperty("previousLeaf")
    abstract DefaultMutableTreeNode getPreviousLeaf();

    @JsonProperty("previousNode")
    abstract DefaultMutableTreeNode getPreviousNode();

    @JsonProperty("previousSibling")
    abstract DefaultMutableTreeNode getPreviousSibling();

    @JsonProperty("siblingCount")
    abstract int getSiblingCount();

    @JsonProperty("isLeaf")
    abstract boolean isLeaf();

    @JsonProperty("isRoot")
    abstract boolean isRoot();
}
  

ObjectMapper:

 ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(DefaultMutableTreeNode.class,DefaultMutableTreeNodeMixIn.class);
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(serverFileTree);
System.out.println(json);
  

(serverFileTree является объектом типа DefaultMutableTreeNode по умолчанию)

Трассировка ошибки:

 at com.fasterxml.jackson.databind.ser.std.ObjectArraySerializer.serializeContents(ObjectArraySerializer.java:252)
at com.fasterxml.jackson.databind.ser.std.ObjectArraySerializer.serialize(ObjectArraySerializer.java:213)
at com.fasterxml.jackson.databind.ser.std.ObjectArraySerializer.serialize(ObjectArraySerializer.java:22)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155) [...]
Caused by: java.lang.StackOverflowError
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.access$100(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:737)
... 1011 more
  

Комментарии:

1. не могли бы вы, пожалуйста, добавить сюда трассировку стека?

2. теперь добавил это в сводку проблем

3. Вместо того, чтобы писать mix-in, я думаю, было бы намного проще написать JsonSerializer for DefaultMutableTreeNode .

Ответ №1:

Этот класс генерирует циклы, когда вы начинаете использовать их методы получения. Чтобы разбить их, вам нужно использовать JsonBackReference аннотацию. Ваш mixin может выглядеть следующим образом:

 @JsonIgnoreProperties(ignoreUnknown = true)
abstract class DefaultMutableTreeNodeMixIn {

    @JsonCreator
    public DefaultMutableTreeNodeMixIn(@JsonProperty Object userObject) {
    }

    @JsonCreator
    public DefaultMutableTreeNodeMixIn(@JsonProperty Object userObject, @JsonProperty boolean allowsChildren) {
    }

    @JsonProperty("childCount")
    abstract int getChildCount();

    @JsonProperty("depth")
    abstract int getDepth();

    @JsonProperty("firstChild")
    @JsonBackReference
    abstract TreeNode getFirstChild();

    @JsonProperty("firstLeaf")
    @JsonBackReference
    abstract DefaultMutableTreeNode getFirstLeaf();

    @JsonProperty("lastChild")
    @JsonBackReference
    abstract TreeNode getLastChild();

    @JsonProperty("lastLeaf")
    @JsonBackReference
    abstract DefaultMutableTreeNode getLastLeaf();

    @JsonProperty("leafCount")
    abstract int getLeafCount();

    @JsonProperty("level")
    abstract int getLevel();

    @JsonProperty("nextLeaf")
    abstract DefaultMutableTreeNode getNextLeaf();

    @JsonProperty("nextNode")
    abstract DefaultMutableTreeNode getNextNode();

    @JsonProperty("nextSibling")
    abstract DefaultMutableTreeNode getNextSibling();

    @JsonProperty("parent")
    abstract TreeNode getParent();

    @JsonProperty("path")
    @JsonBackReference
    abstract TreeNode[] getPath();

    @JsonProperty("previousLeaf")
    abstract DefaultMutableTreeNode getPreviousLeaf();

    @JsonProperty("previousNode")
    abstract DefaultMutableTreeNode getPreviousNode();

    @JsonProperty("previousSibling")
    abstract DefaultMutableTreeNode getPreviousSibling();

    @JsonProperty("siblingCount")
    abstract int getSiblingCount();

    @JsonProperty("isLeaf")
    abstract boolean isLeaf();

    @JsonProperty("isRoot")
    abstract boolean isRoot();
}
  

Но, вероятно, лучший OOP способ — создать new POJO , который представляет ваше дерево, готовое к сериализации и без циклов.

Ответ №2:

Как указано в документации Jackson: https://fasterxml.github.io/jackson-annotations/javadoc/2.6/com/fasterxml/jackson/annotation/JsonProperty.html

public @interface JsonProperty

Аннотация маркера, которая может использоваться для определения нестатического метода как «установщика» или «получателя» для логического свойства (в зависимости от его подписи), или нестатического поля объекта, которое будет использоваться (сериализовано, десериализовано) в качестве логического свойства.

Я действительно думаю, что вы указали методы, которые не являются установщиком или получателем свойств.

Например:

 @JsonProperty("previousNode")
    abstract DefaultMutableTreeNode getPreviousNode();
  

Похоже, что этот метод не не получает свойство, вместо этого он вычисляет узел. Попробуйте удалить все аннотации к методам, чтобы посмотреть, решит ли это проблему.

Комментарии:

1. «getPreviousNode(): возвращает узел, который предшествует этому узлу при обходе в предварительном порядке дерева этого узла.» ( docs.oracle.com/javase/8/docs/api/javax/swing/tree /… ) Я позаботился о том, чтобы все методы возвращали значения при их аннотировании