JsonPath / Jackson:如何将JSON数组反序列化为单个Object(“[1,2,3]” - > Vector3d实例)?

时间:2015-04-06 12:45:03

标签: java jackson jsonpath

抽象

我使用JsonPath选择JSON文件的一部分并将其反序列化为POJO。这适用于字符串,但我无法弄清楚如何让Jackson将三元素数组反序列化为我自己的类(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构建虽然对[{1}}进行反序列化有效,但添加了int[]构造函数@JsonCreator,但仍无法正常工作。有任何想法吗?

3 个答案:

答案 0 :(得分:2)

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

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));
    }
}

答案 1 :(得分:1)

事实证明存在两个问题。首先,我写的地方

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)有效。不幸的是,这需要&gt;像以前一样解析修复JSON文件的时间长度是10倍。如果有人知道更好的解决方案,请告诉我。

答案 2 :(得分:0)

映射您的对象属性&#34; location&#34;到构造函数变量使用@JsonProperty:

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

或命名构造函数变量,与json对象中的prop相同(&#39; location&#39;)

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

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