如何用Jackson反序列化动态JSON字段?

时间:2022-03-10 18:02:10

How can I deserialize JSON a object with dynamic fields (like the one called stories in the code below) into an array of objects?

如何将具有动态字段的对象(如下面代码中称为故事的对象)反序列化为对象数组?

{
   stories: {
      -IhO1742Lki-Pit0snot: {
         sentences: {
            -IhO2fyAEn15XUge6HeY: {
               userName: "Giulia",
               text: "a dude created a new religion called PornDora"
            },
            -IhO2fyAEn15XUge6HeZ: {
               userName: "Will",
               text: "but it was kidnapped by a flying burrito copter"
            },
            -IhO2fyAEn15XUge6HeX: {
               userName: "Jasmine",
               text: "I went this morning at AngelHack hackaton"
            }
         }
      },
      -IhO-gNvUPHpB9fOn-Gm: {
         sentences: {
            -IhO0PBBnJavU2gfMcVO: {
               userName: "Giorgio",
               text: "I woke up alone in a dark alley and a yellow cat was starring at me"
            },
            -IhO11CWL9r6G4Pu8YXx: {
               userName: "Will",
               text: "the cat runned away when a blurred figure approached me and called my name"
            }
         }
      }
   }
}

These below are the Java classes I'm using for deserialization:

以下是我用于反序列化的Java类:

public class Story {

    private String id;
    private List sentences;

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public List getSentences() {
        return sentences;
    }
    public void setSentences(List sentences) {
        this.sentences = sentences;
    }

}
public class Sentence {

    private String id;
    private String text;
    private String userName;



    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getText() {
        return text;
    }
    public void setText(String text) {
        this.text = text;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }

}

1 个解决方案

#1


0  

Do you control the format of the JSON serialization? For example, could you adapt the following format instead:

您是否控制JSON序列化的格式?例如,您可以改为使用以下格式:

{
    "stories": [
        {
            "id": "-IhO1742Lki-Pit0snot", 
            "sentences": [
                {
                    "id": "-IhO2fyAEn15XUge6HeY", 
                    "text": "a dude created a new religion called PornDora", 
                    "userName": "Giulia"
                }
            ]
        )
    ]
}

If you do control the serialization side, you could try using Polymorphic Type Handling (PTH).

如果您确实控制序列化方面,则可以尝试使用多态类型处理(PTH)。

This will give Jackson enough type information to deserialize your objects.

这将为杰克逊提供足够的类型信息来反序列化您的对象。

Also see Tatu's accompanying blog post for background info.

另请参阅Tatu随附的博客文章了解背景信息。


Here is a code snippet for PTH in Jackson 1.x. If you want to use Jackson 2.x, you'll leverage the com.fasterxml packages and need to change the code a little bit.

这是Jackson 1.x中PTH的代码片段。如果你想使用Jackson 2.x,你将利用com.fasterxml包并需要稍微改变代码。

// import com.fasterxml.jackson.annotation.JsonTypeInfo;
// import com.fasterxml.jackson.databind.ObjectMapper;
import org.codehaus.jackson.annotate.JsonTypeInfo;
import org.codehaus.jackson.map.ObjectMapper;

public class MyObjectMapperProvider {
    final ObjectMapper defaultObjectMapper;

    public MyObjectMapperProvider() {
        System.out.println("MyObjectMapperProvider()");


        this.defaultObjectMapper = new ObjectMapper();
        this.defaultObjectMapper.enableDefaultTyping(
            ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_OBJECT
        );
        // .enableDefaultTyping();
        // .enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
        // .enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_OBJECT); 
        // this.defaultObjectMapper.addMixInAnnotations(Bindable.class, MyObjectMapperProvider.MixIn.class);
        // this.defaultObjectMapper.addMixInAnnotations(DataModel.WriteOp.class, MyObjectMapperProvider.MixIn.class);
    }
}

By the way, the json you provided is not actually valid JSON. Try using

顺便说一句,你提供的json实际上并不是有效的JSON。尝试使用

   {
        "stories": {
            "-IhO-gNvUPHpB9fOn-Gm": {
                "sentences": {
                    "-IhO0PBBnJavU2gfMcVO": {
                        "text": "I woke up alone in a dark alley and a yellow cat was starring at me", 
                        "userName": "Giorgio"
                    }, 
                    "-IhO11CWL9r6G4Pu8YXx": {
                        "text": "the cat runned away when a blurred figure approached me and called my name", 
                        "userName": "Will"
                    }
                }
            }, 
            "-IhO1742Lki-Pit0snot": {
                "sentences": {
                    "-IhO2fyAEn15XUge6HeX": {
                        "text": "I went this morning at AngelHack hackaton", 
                        "userName": "Jasmine"
                    }, 
                    "-IhO2fyAEn15XUge6HeY": {
                        "text": "a dude created a new religion called PornDora", 
                        "userName": "Giulia"
                    }, 
                    "-IhO2fyAEn15XUge6HeZ": {
                        "text": "but it was kidnapped by a flying burrito copter", 
                        "userName": "Will"
                    }
                }
            }
        }
    }

You can verify that your json is correct by passing it into python -m json.tool. On OS X, you can copy the json into your clipboard and pass it to json.tool as follows:

您可以通过将json传递给python -m json.tool来验证您的json是否正确。在OS X上,您可以将json复制到剪贴板并将其传递给json.tool,如下所示:

 > pbpaste | python -m json.tool

Here is a small example that leverages Jersey if you want to play around. Use curl http://localhost:7654/sandbox/test to exercise it:

如果你想玩游戏,这是一个利用泽西岛的小例子。使用curl http:// localhost:7654 / sandbox / test来练习它:

MainClass.java

import java.net.URI;

import org.glassfish.grizzly.http.server.HttpServer;
// import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MainClass {
     private static final Logger LOG = LoggerFactory.getLogger(MainClass.class);
     public static final String PATH = "http://localhost:7654/sandbox/";
     public static void main(String[] args) {
          // create a resource config that scans for JAX-RS resources and providers
          // in com.mlbam.monitoring.lsg.data_aggregator package
          final ResourceConfig rc = new ResourceConfig()
                .packages("com.mlbam.internal")
                // .packages("com.mlbam.monitoring.collector.provis")
                .register(MyObjectMapperProvider.class)
                .register(JacksonFeature.class)
          ;

          // create and start a new instance of grizzly http server
          // exposing the Jersey application at BASE_URI
          final HttpServer grizzlyServerV1 = GrizzlyHttpServerFactory.createHttpServer(URI.create(PATH), rc);
          LOG.info("Jersey app started with WADL available at {}application.wadl", PATH);
          try {
                Thread.sleep(10_000);
          } catch (InterruptedException e) { }
          LOG.info("Stopping");
     }
}

MyObjectMapperProvider.java:

// import com.fasterxml.jackson.annotation.JsonTypeInfo;
// import com.fasterxml.jackson.databind.ObjectMapper;
import org.codehaus.jackson.annotate.JsonTypeInfo;
import org.codehaus.jackson.map.ObjectMapper;

import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

@Provider
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> {
    final ObjectMapper defaultObjectMapper;

    public MyObjectMapperProvider() {
         System.out.println("MyObjectMapperProvider()");

         this.defaultObjectMapper = new ObjectMapper();
         this.defaultObjectMapper.enableDefaultTyping(
               ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_OBJECT
         );
         // .enableDefaultTyping();
         // .enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
         // .enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_OBJECT); 
         // this.defaultObjectMapper.addMixInAnnotations(Bindable.class, MyObjectMapperProvider.MixIn.class);
         // this.defaultObjectMapper.addMixInAnnotations(DataModel.WriteOp.class, MyObjectMapperProvider.MixIn.class);
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
         System.out.println("MyObjectMapperProvider.getContext(" + type + ")");
         return this.defaultObjectMapper;
    }


    // @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
    // public static class MixIn {
    // }
}

MyResource.java

import javax.inject.Singleton;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Singleton
@Path("test/")
public class MyResource {
    // public Thing[] state = {new Thing("asdf"), new Thing("foo"), new Thing("bar")};
    public String[] state = {"asdf", "foo", "bar"};

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response test() {
         return Response.ok(state).build();
    }
}

Dependencies (if using maven):

依赖关系(如果使用maven):

<dependencies>
    <!-- Logging -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.5</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.0.13</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>1.0.13</version>
    </dependency>
    <!-- Jersey Specific Dependencies -->
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-grizzly2-http</artifactId>
        <version>2.3.1</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>2.3.1</version>
    </dependency>
</dependencies>

#1


0  

Do you control the format of the JSON serialization? For example, could you adapt the following format instead:

您是否控制JSON序列化的格式?例如,您可以改为使用以下格式:

{
    "stories": [
        {
            "id": "-IhO1742Lki-Pit0snot", 
            "sentences": [
                {
                    "id": "-IhO2fyAEn15XUge6HeY", 
                    "text": "a dude created a new religion called PornDora", 
                    "userName": "Giulia"
                }
            ]
        )
    ]
}

If you do control the serialization side, you could try using Polymorphic Type Handling (PTH).

如果您确实控制序列化方面,则可以尝试使用多态类型处理(PTH)。

This will give Jackson enough type information to deserialize your objects.

这将为杰克逊提供足够的类型信息来反序列化您的对象。

Also see Tatu's accompanying blog post for background info.

另请参阅Tatu随附的博客文章了解背景信息。


Here is a code snippet for PTH in Jackson 1.x. If you want to use Jackson 2.x, you'll leverage the com.fasterxml packages and need to change the code a little bit.

这是Jackson 1.x中PTH的代码片段。如果你想使用Jackson 2.x,你将利用com.fasterxml包并需要稍微改变代码。

// import com.fasterxml.jackson.annotation.JsonTypeInfo;
// import com.fasterxml.jackson.databind.ObjectMapper;
import org.codehaus.jackson.annotate.JsonTypeInfo;
import org.codehaus.jackson.map.ObjectMapper;

public class MyObjectMapperProvider {
    final ObjectMapper defaultObjectMapper;

    public MyObjectMapperProvider() {
        System.out.println("MyObjectMapperProvider()");


        this.defaultObjectMapper = new ObjectMapper();
        this.defaultObjectMapper.enableDefaultTyping(
            ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_OBJECT
        );
        // .enableDefaultTyping();
        // .enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
        // .enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_OBJECT); 
        // this.defaultObjectMapper.addMixInAnnotations(Bindable.class, MyObjectMapperProvider.MixIn.class);
        // this.defaultObjectMapper.addMixInAnnotations(DataModel.WriteOp.class, MyObjectMapperProvider.MixIn.class);
    }
}

By the way, the json you provided is not actually valid JSON. Try using

顺便说一句,你提供的json实际上并不是有效的JSON。尝试使用

   {
        "stories": {
            "-IhO-gNvUPHpB9fOn-Gm": {
                "sentences": {
                    "-IhO0PBBnJavU2gfMcVO": {
                        "text": "I woke up alone in a dark alley and a yellow cat was starring at me", 
                        "userName": "Giorgio"
                    }, 
                    "-IhO11CWL9r6G4Pu8YXx": {
                        "text": "the cat runned away when a blurred figure approached me and called my name", 
                        "userName": "Will"
                    }
                }
            }, 
            "-IhO1742Lki-Pit0snot": {
                "sentences": {
                    "-IhO2fyAEn15XUge6HeX": {
                        "text": "I went this morning at AngelHack hackaton", 
                        "userName": "Jasmine"
                    }, 
                    "-IhO2fyAEn15XUge6HeY": {
                        "text": "a dude created a new religion called PornDora", 
                        "userName": "Giulia"
                    }, 
                    "-IhO2fyAEn15XUge6HeZ": {
                        "text": "but it was kidnapped by a flying burrito copter", 
                        "userName": "Will"
                    }
                }
            }
        }
    }

You can verify that your json is correct by passing it into python -m json.tool. On OS X, you can copy the json into your clipboard and pass it to json.tool as follows:

您可以通过将json传递给python -m json.tool来验证您的json是否正确。在OS X上,您可以将json复制到剪贴板并将其传递给json.tool,如下所示:

 > pbpaste | python -m json.tool

Here is a small example that leverages Jersey if you want to play around. Use curl http://localhost:7654/sandbox/test to exercise it:

如果你想玩游戏,这是一个利用泽西岛的小例子。使用curl http:// localhost:7654 / sandbox / test来练习它:

MainClass.java

import java.net.URI;

import org.glassfish.grizzly.http.server.HttpServer;
// import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MainClass {
     private static final Logger LOG = LoggerFactory.getLogger(MainClass.class);
     public static final String PATH = "http://localhost:7654/sandbox/";
     public static void main(String[] args) {
          // create a resource config that scans for JAX-RS resources and providers
          // in com.mlbam.monitoring.lsg.data_aggregator package
          final ResourceConfig rc = new ResourceConfig()
                .packages("com.mlbam.internal")
                // .packages("com.mlbam.monitoring.collector.provis")
                .register(MyObjectMapperProvider.class)
                .register(JacksonFeature.class)
          ;

          // create and start a new instance of grizzly http server
          // exposing the Jersey application at BASE_URI
          final HttpServer grizzlyServerV1 = GrizzlyHttpServerFactory.createHttpServer(URI.create(PATH), rc);
          LOG.info("Jersey app started with WADL available at {}application.wadl", PATH);
          try {
                Thread.sleep(10_000);
          } catch (InterruptedException e) { }
          LOG.info("Stopping");
     }
}

MyObjectMapperProvider.java:

// import com.fasterxml.jackson.annotation.JsonTypeInfo;
// import com.fasterxml.jackson.databind.ObjectMapper;
import org.codehaus.jackson.annotate.JsonTypeInfo;
import org.codehaus.jackson.map.ObjectMapper;

import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

@Provider
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> {
    final ObjectMapper defaultObjectMapper;

    public MyObjectMapperProvider() {
         System.out.println("MyObjectMapperProvider()");

         this.defaultObjectMapper = new ObjectMapper();
         this.defaultObjectMapper.enableDefaultTyping(
               ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_OBJECT
         );
         // .enableDefaultTyping();
         // .enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
         // .enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_OBJECT); 
         // this.defaultObjectMapper.addMixInAnnotations(Bindable.class, MyObjectMapperProvider.MixIn.class);
         // this.defaultObjectMapper.addMixInAnnotations(DataModel.WriteOp.class, MyObjectMapperProvider.MixIn.class);
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
         System.out.println("MyObjectMapperProvider.getContext(" + type + ")");
         return this.defaultObjectMapper;
    }


    // @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
    // public static class MixIn {
    // }
}

MyResource.java

import javax.inject.Singleton;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Singleton
@Path("test/")
public class MyResource {
    // public Thing[] state = {new Thing("asdf"), new Thing("foo"), new Thing("bar")};
    public String[] state = {"asdf", "foo", "bar"};

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response test() {
         return Response.ok(state).build();
    }
}

Dependencies (if using maven):

依赖关系(如果使用maven):

<dependencies>
    <!-- Logging -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.5</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.0.13</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>1.0.13</version>
    </dependency>
    <!-- Jersey Specific Dependencies -->
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-grizzly2-http</artifactId>
        <version>2.3.1</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>2.3.1</version>
    </dependency>
</dependencies>