0

I have an Abstract class with many concrete implementations:

public abstract Ticket {
   private Long id;
   private Currency fine;
   ...
}

public class SpeedingTicket extends Ticket {
   public Currency getFine(){
      // Expensive!
      ...
   }
}

public class ParkingTicket  extends Ticket {
   public Currency getFine(){
      // Eh, not so bad
      ...
   }
}

When the concrete classes are serialized into JSON, it is wrapped with the classes simple name (speedingTickets or parkingTickets):

"_embedded": {
   "speedingTickets" :
    [{
      "id":1,
      "fine": "$190",
       ...,
    },
    {
      "id":2,
      "fine": "$100",
       ...,
    }]
}

or

"_embedded": {
   "parkingTickets" :[{
      "id":100,
      "fine": "$15",
       ...,
    }]
}

Since I do not know, at runtime, which Ticket implementation I am receiving back, how can I parse the JSON out using the JSON Response API given the array is wrapped with the concrete implementations simple name?

I have a hack where I take the String value of the JSON and do String operations (substring, indexOf, etc) on it to return only what's in between the braces ("[...]"). I know there's a better way to do this...



After some research, I think I'll try the following tomorrow to see if it works:

JsonNode rootNode = mapper.readTree(jsonResponse);
String classImpl = Iterables.get(rootNode.get("_embedded").fields(), 0).textValue()

I can then say List<Ticket> tickets = response.readAsList(jsonResponse, "_embedded",classImpl) which should allow me to parse the JSON into a List

Dan
  • 979
  • 1
  • 8
  • 29
  • Possible duplicate of [Gson - Read a value with two different keys](http://stackoverflow.com/questions/35739713/gson-read-a-value-with-two-different-keys) – Bharatesh Apr 12 '17 at 04:46
  • My question is about parsing a json aray that has a key that is dynamically generated. Your links is about properties. – Dan Apr 12 '17 at 04:56

3 Answers3

0

If you're using Jackson (as your tag suggests), you want to use Polymorphic Deserialization - which is exactly the problem of knowing how to deserialize to the correct subtype.

For example:

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

What this essentially does is include the class name in your JSON, so the deserializer has enough information to properly choose the subclass to instantiate. Something like this:

"_embedded": {
   "parkingTickets" :[{
      "_type": "ParkingTicket.class",
      "id":100,
      "fine": "$15",
       ...,
    }]
}
nighthawk454
  • 943
  • 12
  • 20
  • Yes, I am doing this. I am using @JsonTypeInfo along with an existing property value to determine the implementation. My question however is related to a functional unit test that I am working on. I make the mock call out through the API, however I cannot turn the response back into the object since the key ('parkingTickets' or 'speedingTickets') changes based upon its implementation. – Dan Apr 12 '17 at 04:26
  • It would help if you gave a more comprehensive summary in your question. I'm assuming you mean the key changes based on the API's implementation? If you control both the API and the Client, you should put the models in a shared library so the serialization/deserialization is definitely the same on both ends. If you don't control the API, the best you can do is code it to the docs. – nighthawk454 Apr 12 '17 at 04:31
  • I mean the key changes based on the concrete classes name. So the key "speedingTickets" within the json is created by the `SpeedingTicket` implementation. Same with `ParkingTickets`. The value after "_embedded" changes based on the implementations class name – Dan Apr 12 '17 at 04:35
  • why not do: `"tickets": [{...}, {...}, {...}` and put all the types in one list? Otherwise, make a `ParkingTickets` (plural) class and serialize that. – nighthawk454 Apr 12 '17 at 04:41
  • The key is what's wrapping the Object which helps the framework select the concrete implementation class. Unfortunately, I cannot change that value – Dan Apr 12 '17 at 04:46
0

You can just check the type by checking the variable the response contains.

JSONObject jsonObj = new JSONObject(response);
if(jsonObj.has("speedingTickets")){
    // parse speedingTickets
}else if(jsonObj.has("parkingTickets")){
    // parse parkingtickets
}
Rajesh Gosemath
  • 1,812
  • 1
  • 17
  • 31
  • I thought about doing this, however there are 20+ implementation types. I don't think this would be practical on a large scale – Dan Apr 12 '17 at 04:28
-1

A JSON object is an unordered set of key/value pairs. A JSON array is an ordered collection of values. The values themselves could be objects or arrays.

In java it is easy to parse json with org.json library https://github.com/stleary/JSON-java

Short example how to parse json array:

String str = "{ \"number\": [3, 4, 5, 6] }";
JSONObject obj = new JSONObject(str);
JSONArray arr = obj.getJSONArray("number");
for (int i = 0; i < arr.length(); i++)
    System.out.println(arr.getInt(i));
Jay Smith
  • 2,331
  • 3
  • 16
  • 27
  • I think you're missing the point of my question. What you have above works IF you know that "number" is the key. In my situation, the key is dynamic (determined by the simple name of the implementing class) – Dan Apr 12 '17 at 04:14