简体   繁体   中英

How to convert JSON to Java object if I do not know all json fields before parsing?

My service can receive several different jsons, such as:

{
   "event":"conversation_started",
   "context":"context information",
   "user":{
      "id":"01234567890A=",
      "name":"John McClane",
      "avatar":"http://avatar.example.com",
      "country":"UK",
      "language":"en",
      "api_version":1
   },
   "subscribed":false
}

or

 {
   "event":"message",
   "message":{
      "type":"text",
      "text":"a message to the service",
      "location":{
         "lat":12.34,
         "lon":12.34
      }
   }
}

or several else jsons. The only field that is the same for all jsons is "event". All other fields can be different (depends on "event" value).

So the question is: how to convert those jsons to java objects (without making messy code)? The only way I know is to manually check "event" value (like json.startsWith("{\n\"event\":\"message\"") but I'm sure that there is any simple decision for doing this.

UPD: If you don't want to convert JSON String to JAVA Object via declaring a POJO, you can parse it to JSONObject (com.alibaba.fastjson.JSONObject)

public class Event {

    public static void main(String[] args) {
        String jsonA = "{\"event\":\"conversation_started\",\"context\":\"context information\",\"user\":{\"id\":\"01234567890A=\",\"name\":\"John McClane\",\"avatar\":\"http://avatar.example.com\",\"country\":\"UK\",\"language\":\"en\",\"api_version\":1},\"subscribed\":false}";
        String jsonB = "{\"event\":\"message\",\"message\":{\"type\":\"text\",\"text\":\"a message to the service\",\"location\":{\"lat\":12.34,\"lon\":12.34}}}";

        JSONObject jsonObject = JSONObject.parseObject(jsonA);
        String event = jsonObject.getString("event");
        if (event.equals("message")) {
            //do what you want to do
            System.out.println("message event......");
        } else if ("conversation_started".equals(event)) {
            System.out.println("context information event......");
        }
    }
}

Declaring a class of Event as below, and then convert JSON String to a Event JAVA object.

@Data
public class Event {
    private String event;
    private String context;
    private User user;
    private boolean subscribed;
    private Message message;

    @Data
    public static class User {
        private String id;
        private String name;
        private String avatar;
        private String country;
        private String language;
        private int api_version;
    }

    @Data
    public static class Message {
        private String type;
        private String text;
        private Location location;

        @Data
        public static class Location {
            private double lat;
            private double lon;
        }
    }

    public static void main(String[] args) {
        String jsonA = "{\"event\":\"conversation_started\",\"context\":\"context information\",\"user\":{\"id\":\"01234567890A=\",\"name\":\"John McClane\",\"avatar\":\"http://avatar.example.com\",\"country\":\"UK\",\"language\":\"en\",\"api_version\":1},\"subscribed\":false}";
        String jsonB = "{\"event\":\"message\",\"message\":{\"type\":\"text\",\"text\":\"a message to the service\",\"location\":{\"lat\":12.34,\"lon\":12.34}}}";

        ObjectMapper objectMapper = new ObjectMapper();
        try {
            Event eventA = objectMapper.readValue(jsonA, new TypeReference<Event>() {
            });
            System.out.println(objectMapper.writeValueAsString(eventA));

            Event eventB = objectMapper.readValue(jsonB, new TypeReference<Event>() {
            });
            System.out.println(objectMapper.writeValueAsString(eventB));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Use a JSON object. This is dynamic and can load any json. Then you can reference the event field consistently

Example 1

 //import java.util.ArrayList;
 //import org.bson.Document;


 Document root = Document.parse("{ \"event\" : \"conversation_started\", \"context\" : \"context information\", \"user\" : { \"id\" : \"01234567890A=\", \"name\" : \"John McClane\", \"avatar\" : \"http://avatar.example.com\", \"country\" : \"UK\", \"language\" : \"en\", \"api_version\" : 1 }, \"subscribed\" : false }");

 System.out.println(((String)root.get("event")));

Example 2

 //import java.util.ArrayList;
 //import org.bson.Document;


 Document root = Document.parse("{ \"event\" : \"message\", \"message\" : { \"type\" : \"text\", \"text\" : \"a message to the service\", \"location\" : { \"lat\" : 12.34, \"lon\" : 12.34 } } }");

 System.out.println(((String)root.get("event")));

There are three ways I've done this. The first is to do what you're suggesting - parse the JSON, check the type, and create the object. Be very careful with using a String parser as you may or may not have things like new lines. Instead, do something like:

ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(eventString);
String eventType = jsonNode.get("event").asText();

if( eventType.equalsIgnoreCase("conversation_started")) {
    // create ConversationStarted object using something like:
    ConversationStarted conversationStarted = objectMapper.readValue( eventString, ConversationStarted.class );
}

This, of course, requires all classes to have a concrete POJO to allow for deserialization.

Another way is to do what many other programming languages do and have a key/value map. There are a few ways to do this. One is with the Jackson libraries:

Map<String, Object> map = objectMapper.readValue(eventString, new TypeReference<Map<String,Object>>(){});

Map<String, Object> user = (Map<String, Object>) map.get("user");

System.out.println( "conversation started - avatar is " + user.get("avatar"));

That way you can pass around the Map and extract as needed. Note that you still need to understand the structure of the JSON but you don't need to have a POJO for it.

Lastly is a variation on the second solution. Using JSONPath you can pull out what you need directly. Again you will want to first check out which type of event you have. Something like:

if( JsonPath.read(eventString, "$.event").equals("conversation_started") ) {
    String avatar = JsonPath.read(eventString, "$.user.avatar");
    System.out.println("conversation started - avatar is " + avatar);
 }

The last two methods require you to pull out values one at a time as shown. The first solution gives you a full object to work with. It is your call as to what works best in your environment.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM