【问题标题】:Can Jackson be used to create a type-tree from a class?杰克逊可以用来从一个类中创建一个类型树吗?
【发布时间】:2021-11-06 18:25:09
【问题描述】:

我正在寻找一种使用 Jackson 创建类型树而不是值树的方法。 我曾认为这是可能的,但我遇到了一个问题,Jackson 在遇到具有 null 作为值的字段时会创建一个 NullNode 对象。

我感兴趣的是类型,而不是值。目前,我正在执行以下解决方法,因为我无法为 Jackson 提供构建树的类:

package org.example.jackson.typetree;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class FunctioningTest {
    static class SomeClass{
        public Integer integer;
        public String string;
    }

    @Test
    void extractTypeTree() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        final Constructor<?> constructor = SomeClass.class.getDeclaredConstructor();
        final Object o =  constructor.newInstance((Object[]) null);
        final var jsonNode = new ObjectMapper().valueToTree(o);
        final var fields = jsonNode.fields();
        while(fields.hasNext()){
            final var child = fields.next();
            if(child.getValue().isIntegralNumber() || child.getValue().isTextual()){
                System.out.println("Nice!");
            }else if(child.getValue().isNull()){
                System.out.println("Booooh...!");
            }
        }
    }
}

正如我所提到的,这会产生一个 ObjectNode 实例,它有 2 个 NullNode 实例作为子节点。然而,我想要的是获得一个带有 IntNode/NumericNode 和 TextNode 的 ObjectNode,而不管 SomeClass 实例中字段的实际值如何。

Jackson 可以用来做这件事吗?

【问题讨论】:

标签: java types tree jackson


【解决方案1】:

我又花了几个小时试图找出一种遍历类的方法,并且找到了一种对我来说足够好的方法。

为了强调,Jackson 没有创建类型树。相反,它有几种访问者类型,可用于访问序列化程序。 Jackson 中的序列化器是将类的实例序列化为某些输出(JSON、YAML 等)的方法。当访问这些序列化器时,我们可以依次检查对象的属性并依次访问它们。

下面是这个机制的基本实现,可以调整它以手动构建类型树。我使用了 jackson-databind:2.12.1,但我相信该机制是在 jackson-databind:2.2.0 中引入的。

package org.example.jackson.type.tree;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsonFormatVisitors.*;
import com.fasterxml.jackson.databind.ser.SerializerFactory;
import org.junit.jupiter.api.Test;

import java.util.Set;

public class FunctioningTest {

    @Test
    void extractTypeTree() throws JsonMappingException {
        final var objectMapper = new ObjectMapper();
        final var serializerProvider = objectMapper.getSerializerProviderInstance();
        final var javaType = objectMapper.constructType(SomeClass.class);
        final var serializerFactory = objectMapper.getSerializerFactory();
        final var typeSerializer = serializerFactory.createSerializer(serializerProvider, javaType);
        final var FieldVisitor = new FieldVisitor(serializerProvider, serializerFactory);
        typeSerializer.acceptJsonFormatVisitor(FieldVisitor, javaType);

    }

    static class SomeClass {
        public Integer integer;
        public String string;
        public FieldClass fieldClass;
    }

    static class FieldClass {
        public boolean aBoolean;
        public double floatingPoint;
    }

    static class FieldVisitor extends JsonFormatVisitorWrapper.Base implements JsonFormatVisitorWrapper {

        private final SerializerFactory serializerFactory;

        public FieldVisitor(final SerializerProvider provider, final SerializerFactory serializerFactory) {
            super(provider);
            this.serializerFactory = serializerFactory;
        }

        @Override
        public JsonObjectFormatVisitor expectObjectFormat(JavaType javaType) throws JsonMappingException {
            System.out.println("FieldVisitor (Object): " + javaType);
            final var objectVisitor = new ObjectVisitor(getProvider());
            final var objectSerializer = serializerFactory.createSerializer(getProvider(), javaType);
            final var properties = objectSerializer.properties();
            while (properties.hasNext()) {
                final var property = properties.next();
                final var propertyType = property.getType();
                final var propertySerializer = serializerFactory.createSerializer(getProvider(), propertyType);
                propertySerializer.acceptJsonFormatVisitor(this, propertyType);
            }
            return objectVisitor;
        }

        @Override
        public JsonArrayFormatVisitor expectArrayFormat(JavaType type) throws JsonMappingException {
            return null;
        }

        @Override
        public JsonStringFormatVisitor expectStringFormat(JavaType type) throws JsonMappingException {
            System.out.println("FieldVisitor (String): " + type);
            return new StringVisitor();
        }

        @Override
        public JsonNumberFormatVisitor expectNumberFormat(JavaType type) throws JsonMappingException {
            System.out.println("FieldVisitor (Number): " + type);
            return new NumberVisitor();
        }

        @Override
        public JsonIntegerFormatVisitor expectIntegerFormat(JavaType type) throws JsonMappingException {
            System.out.println("FieldVisitor (Integer): " + type);
            return new IntegerVisitor();
        }

        @Override
        public JsonBooleanFormatVisitor expectBooleanFormat(JavaType type) throws JsonMappingException {
            System.out.println("FieldVisitor (Boolean): " + type);
            return new BooleanVisitor();
        }

        @Override
        public JsonNullFormatVisitor expectNullFormat(JavaType type) throws JsonMappingException {
            System.out.println("Null: " + type);
            return null;
        }

        @Override
        public JsonAnyFormatVisitor expectAnyFormat(JavaType type) throws JsonMappingException {
            return null;
        }

        @Override
        public JsonMapFormatVisitor expectMapFormat(JavaType type) throws JsonMappingException {
            return null;
        }

    }

    static class ObjectVisitor extends JsonObjectFormatVisitor.Base implements JsonObjectFormatVisitor {

        public ObjectVisitor(SerializerProvider serializerProvider) {
            super(serializerProvider);
        }

        @Override
        public void property(BeanProperty writer) throws JsonMappingException {
            System.out.println("ObjectVisitor: " + writer);
        }

        @Override
        public void property(String name, JsonFormatVisitable handler, JavaType propertyTypeHint) throws JsonMappingException {
            System.out.println("ObjectVisitor: " + String.join(", ", name, handler.toString(), propertyTypeHint.toString()));
        }

        @Override
        public void optionalProperty(BeanProperty writer) throws JsonMappingException {
            System.out.println("ObjectVisitor (optional): " + writer);
        }

        @Override
        public void optionalProperty(String name, JsonFormatVisitable handler, JavaType propertyTypeHint) throws JsonMappingException {
            System.out.println("ObjectVisitor (optional): " + String.join(", ", name, handler.toString(), propertyTypeHint.toString()));
        }
    }

    static class StringVisitor implements JsonStringFormatVisitor {
        @Override
        public void format(JsonValueFormat format) {
            System.out.println("StringVisitor (format): " + format);
        }

        @Override
        public void enumTypes(Set<String> enums) {
            System.out.println("StringVisitor (enums): " + enums);
        }
    }

    static class IntegerVisitor implements JsonIntegerFormatVisitor {

        @Override
        public void numberType(JsonParser.NumberType type) {
            System.out.println("IntegerVisitor (numberType): " + type);
        }

        @Override
        public void format(JsonValueFormat format) {
            System.out.println("IntegerVisitor (format): " + format);
        }

        @Override
        public void enumTypes(Set<String> enums) {
            System.out.println("IntegerVisitor (enums): " + enums);
        }
    }

    static public class BooleanVisitor implements JsonBooleanFormatVisitor {
        @Override
        public void format(JsonValueFormat format) {
            System.out.println("BooleanVisitor (format): " + format);
        }

        @Override
        public void enumTypes(Set<String> enums) {
            System.out.println("BooleanVisitor (enums): " + enums);
        }
    }

    static class NumberVisitor implements JsonNumberFormatVisitor {
        @Override
        public void numberType(JsonParser.NumberType type) {
            System.out.println("NumberVisitor (numberType): " + type);
        }

        @Override
        public void format(JsonValueFormat format) {
            System.out.println("NumberVisitor (format): " + format);
        }

        @Override
        public void enumTypes(Set<String> enums) {
            System.out.println("NumberVisitor (enums): " + enums);
        }
    }
}

哪些输出:

FieldVisitor (Object): [simple type, class io.serpentes.examples.schema.sources.jackson.FunctioningTest$SomeClass]
FieldVisitor (Integer): [simple type, class java.lang.Integer]
IntegerVisitor (numberType): INT
FieldVisitor (String): [simple type, class java.lang.String]
FieldVisitor (Object): [simple type, class io.serpentes.examples.schema.sources.jackson.FunctioningTest$FieldClass]
FieldVisitor (Boolean): [simple type, class boolean]
FieldVisitor (Number): [simple type, class double]
NumberVisitor (numberType): DOUBLE
ObjectVisitor (optional): property 'aBoolean' (field "io.serpentes.examples.schema.sources.jackson.FunctioningTest$FieldClass#aBoolean, no static serializer)
ObjectVisitor (optional): property 'floatingPoint' (field "io.serpentes.examples.schema.sources.jackson.FunctioningTest$FieldClass#floatingPoint, no static serializer)
ObjectVisitor (optional): property 'integer' (field "io.serpentes.examples.schema.sources.jackson.FunctioningTest$SomeClass#integer, no static serializer)
ObjectVisitor (optional): property 'string' (field "io.serpentes.examples.schema.sources.jackson.FunctioningTest$SomeClass#string, no static serializer)
ObjectVisitor (optional): property 'fieldClass' (field "io.serpentes.examples.schema.sources.jackson.FunctioningTest$SomeClass#fieldClass, no static serializer)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-04
    • 2013-06-03
    • 1970-01-01
    • 2012-08-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多