【问题标题】:JsonPath/Jackson: how to deserialize JSON array to a single Object ("[1,2,3]" -> Vector3d instance)?JsonPath/Jackson:如何将 JSON 数组反序列化为单个对象(“[1,2,3]”-> Vector3d 实例)?
【发布时间】:2015-04-06 12:45:03
【问题描述】:

摘要

我正在使用 JsonPath 选择 JSON 文件的一部分并将其反序列化为 POJO。这适用于字符串,但我不知道如何让杰克逊将三元素数组反序列化为我自己的类 (Vector3d) 的单个实例。 Jackson 文档中的所有示例似乎都涉及将 JSON 中的元素转换为一个 Java 对象。有什么想法吗?

文档内容

JsonPath documentation 提到了这一点

如果您将 JsonPath 配置为使用 JacksonMappingProvider,您甚至可以将 JsonPath 输出直接映射到 POJO。

Book book = JsonPath.parse(json).read("$.store.book[0]", Book.class);

但我无法让它工作。

我的尝试

我的Vector3d 类如下所示(注意@JsonCreator 构造函数):

import com.fasterxml.jackson.annotation.JsonCreator;

public final class Vector3d {
    private final int x, y, z;

    public Vector3d(int x, int y, int z) { 
        this.x = x; this.y = y; this.z = z;
    }

    @JsonCreator
    public Vector3d(int[] values) { 
        this(values[0], values[1], values[2]); 
    }
}

对于这个例子(我使用的实际文件更复杂):

{
    type: "viper",
    location: [
        20,
        173,
        153
    ]
}

我可以将“位置”向量作为一个三元素数组,如下所示(这段代码过于冗长,但只是为了让下一步保持小规模):

package main;

import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.internal.JsonReader;
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
import com.jayway.jsonpath.spi.mapper.MappingProvider;

import java.util.Arrays;

public class Tester {
    public static void main(String[] args) throws Exception {
        Configuration configuration = Configuration.defaultConfiguration();
        MappingProvider mappingProvider = new JacksonMappingProvider();
        configuration.mappingProvider(mappingProvider);
        JsonReader jsonReader = new JsonReader(configuration);
        DocumentContext documentContext = jsonReader.parse("{\n" +
                "\ttype: \"viper\",\n" +
                "\tlocation: [\n" +
                "\t\t20,\n" +
                "\t\t173,\n" +
                "\t\t153\n" +
                "\t]\n" +
                "}");
        int[] data = documentContext.read("$.location", int[].class);

        System.out.println(Arrays.toString(data));
    }
}

但如果我在最后一行使用 Vector3d 而不是 int[],我会在 read() 调用期间遇到异常:

java.lang.RuntimeException: Invalid or non Implemented status createArray() in class net.minidev.json.writer.BeansMapper$Bean
    at net.minidev.json.writer.JsonReaderI.createArray(JsonReaderI.java:98)
    at net.minidev.json.parser.JSONParserBase.readArray(JSONParserBase.java:235)
    at net.minidev.json.parser.JSONParserBase.readFirst(JSONParserBase.java:298)
    at net.minidev.json.parser.JSONParserBase.parse(JSONParserBase.java:154)
    at net.minidev.json.parser.JSONParserString.parse(JSONParserString.java:58)
    at net.minidev.json.parser.JSONParser.parse(JSONParser.java:261)
    at net.minidev.json.JSONValue.parse(JSONValue.java:206)
    at com.jayway.jsonpath.spi.mapper.JsonSmartMappingProvider.map(JsonSmartMappingProvider.java:86)
    at com.jayway.jsonpath.internal.JsonReader.convert(JsonReader.java:174)
    at com.jayway.jsonpath.internal.JsonReader.read(JsonReader.java:140)
    at main.Tester.main(Tester.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

我很困惑 Vector3d 构造不起作用,尽管反序列化为 int[] 有效,并添加了采用 int[]@JsonCreator 构造函数。有任何想法吗?

【问题讨论】:

    标签: java jackson jsonpath


    【解决方案1】:

    不知道内部非静态类是否存在问题。这对我有用:

    public class VectorTest {
    
        private static final String JSON = "{\n" +
                "    \"type\": \"viper\",\n" +
                "    \"location\": [\n" +
                "        20,\n" +
                "        173,\n" +
                "        153\n" +
                "    ]\n" +
                "}";
    
        static {
            Configuration.setDefaults(new Configuration.Defaults() {
                private final JsonProvider jsonProvider = new JacksonJsonProvider();
                private final MappingProvider mappingProvider = new JacksonMappingProvider();
                private final Set<Option> options = EnumSet.noneOf(Option.class);
    
                public JsonProvider jsonProvider() {
                    return jsonProvider;
                }
    
                @Override
                public MappingProvider mappingProvider() {
                    return mappingProvider;
                }
    
                @Override
                public Set<Option> options() {
                    return options;
                }
            });
        }
    
        public static final class Vector3d {
            public final int x, y, z;
    
            @JsonCreator
            public Vector3d(int[] values) {
                this.x = values[0];
                this.y = values[1];
                this.z = values[2];
            }
        }
    
        @Test
        public void a_test() {
            Vector3d vector = JsonPath.parse(JSON).read("$.location", Vector3d.class);
    
            Assert.assertThat(vector.x, is(20));
            Assert.assertThat(vector.y, is(173));
            Assert.assertThat(vector.z, is(153));
        }
    }
    

    【讨论】:

    • 您的方法有效,谢谢!我认为我的问题是我没有使用JacksonJsonProvider(我不知道这是必要的),加上我忽略了调用configuration.mappingProvider(mappingProvider)的结果的错误;我以为它是一个 setter,但它是一个 builder 方法。我所有的类都是顶级的,所以实例类没有问题。
    【解决方案2】:

    原来有两个问题。首先,我写的地方

    configuration.mappingProvider(mappingProvider);
    

    我应该写:

    configuration = configuration.mappingProvider(mappingProvider);
    

    其次,我无法让注释工作,但我确实使用自定义反序列化器进行了管理。由于我不希望我的值类与特定的 JSON 序列化格式相关联,因此我将其定义为 mixin:

    class JsonReaders {
        private JsonReaders() { } 
    
        static class MixinModule extends SimpleModule {
            static class Vector3dDeserializer extends JsonDeserializer<Vector3d> {
                @Override
                public Vector3d deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
                    int[] values = jp.readValueAs(int[].class);
                    return new Vector3d(values[0], values[1], values[2]);
                }
            }
    
            @Override
            public void setupModule(SetupContext context) {
                super.setupModule(context);
                SimpleDeserializers deserializers = new SimpleDeserializers();
                deserializers.addDeserializer(Vector3d.class, new Vector3dDeserializer());
                context.addDeserializers(deserializers);
            }
        }
    
        public static Configuration createConfiguration() {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.registerModule(new MixinModule());
            MappingProvider mappingProvider = new JacksonMappingProvider(objectMapper);
            Configuration configuration = Configuration.defaultConfiguration()
                    .mappingProvider(mappingProvider);
            return configuration;
        }
    
        public static JsonReader createReader() {
            JsonReader result = new JsonReader(createConfiguration());
            return result;
        }
    }
    

    使用上面定义的 JsonReader,代码 jsonReader.read("$.location", Vector3d.class) 可以工作。不幸的是,通过这种方式,解析修复 JSON 文件所需的时间是以前的 10 倍以上。如果有人知道更好的解决方案,请告诉我。

    【讨论】:

      【解决方案3】:

      要将您的对象属性“位置”映射到构造函数变量,请使用@JsonProperty:

      @JsonCreator
      public Vector3d(@JsonProperty("location") Integer[] values) {
          this(values[0], values[1], values[2]);
      }
      

      或命名构造函数变量与 json object('location') 中的 prop 相同

      @JsonCreator
      public Vector3d(Integer[] location) {
          this(location[0], location[1], location[2]);
      }
      

      我也不确定 int[] 是否可以序列化,所以我使用了 Integer[]

      【讨论】:

      • 我专门根据数组反序列化什么,没有键名(“位置”)。抱歉,我应该给出一个更大的 JSON 示例来澄清一下:在我的 JSON 文件中有许多不同的属性,它们都是三元素数组。我可以使用 JsonPath 将它们全部选中,并且我想将它们反序列化为 Vector3d 对象。实际上,Jackson 只能看到片段 { [20, 173, 153] },没有别的。用 Jackson 反序列化它是否有意义,还是我应该只反序列化为 int[] 并自己做剩下的事情?
      猜你喜欢
      • 1970-01-01
      • 2018-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-27
      • 2023-03-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多