3

I have a JSON stream being generated by a server side C++ program that is currently in development. I've been given a sample of the resulting JSON and I am concerned that I will have to parse the json by hand, that I won't be able to use normal class mapping provided by tools such as GSON or Jackson.

Please take a look at the following (somewhat) contrived example they have provided. The sections I'm concerned with are the meta data "serie" array having different parameters. The key - "key" for example is present in only one of the array elements. Will this not cause issues trying to map this array to a collection of a specific class?

Lastly, I am concerned that the "point" object is not similar. I have very limited understanding of JSON (being an old fashioned java swing developer) but the fact that the "point" key value pairs can be different - is a problem.

The whole idea for this json stream is to describe a table, with ways of showing progress and to provide a mechanism for asking for "more" from the underlying hardware. Also if you are wondering why, I am sharing this datastream with a thin client (html browser).

So am I correct that this will not easily convert to java objects?

{
  "abort": "abort;session=sessionname",
  "data": {
    "metadata": [
      {
        "protocol": "HTTP",
        "serie": [
          {
            "name": "k1",
            "description": "xDR ID",
            "tooltip": "ASDR Unique Identifier - UiD",
            "type": "int64",
            "key": "1"
          },
          {
            "name": "c1",
            "description": "Answered",
            "tooltip": "Request with Response",
            "type": "bool"
          },
          {
            "name": "c2",
            "description": "Active",
            "tooltip": "Session status: active or closed/down",
            "type": "bool"
          }
        ]
      },
      {
        "protocol": "DNS",
        "serie": [
          {
            "name": "k1",
            "description": "xDR ID",
            "tooltip": "ASDR Unique Identifier - UiD",
            "type": "int64",
            "key": "1"
          },
          {
            "name": "k2",
            "description": "Transaction ID",
            "type": "int64",
            "key": "1",
            "display": "number"
          },
          {
            "name": "k3",
            "description": "Client",
            "tooltip": "Source IP Address",
            "type": "string",
            "key": "1",
            "display": "ip"
          }
        ]
      }
    ],
    "summary": [
      {
        "timestamp": "1331192727",
        "protocol": "HTTP",
        "activity": "www.google.com",
        "results": "OK",
        "point": {
          "k1": "1",
          "c1": "true",
          "c2": "true"
        }
      },
      {
        "timestamp": "1331192727",
        "protocol": "DNS",
        "activity": "www.google.com",
        "results": "OK",
        "point": {
          "k1": "1",
          "k2": "1.1.4.229"
        }
      }
    ]
  },
  "progress": {
    "perc": "100"
  },
  "more": "13,39,1331192727,1331192760,27236,1.1.4.229,limit=1000,session=sessionname"
}

Thank you for any advice you can provide.

-D Klotz

D-Klotz
  • 1,973
  • 1
  • 15
  • 37

3 Answers3

4

With GSON, assuming that the class you are deserializing into has fields for all the names that appear in the JSON, the fields not found in the JSON will just be left null:

https://sites.google.com/site/gson/gson-user-guide#TOC-Finer-Points-with-Objects

"While deserialization, a missing entry in JSON results in setting the corresponding field in the object to null"

Things get a little more complicated if arbitrary field names are allowed in the JSON - for example, if Point allows c1, c2, ... cn. But you can handle this with a custom deserializer.

https://sites.google.com/site/gson/gson-user-guide#TOC-Writing-a-Deserializer

Edit:

Here's how you might write a custom deserializer for Point:

private class DateTimeDeserializer implements JsonDeserializer<Point> {
    public Point deserialize(JsonElement json, Type typeOfT, 
            JsonDeserializationContext context) throws JsonParseException {
        List<PointPart> parts = Lists.newArrayList();

        for(Map.Entry<String,JsonElement> entry : 
                json.getAsJsonObject().entrySet()) {
            char type = ;
            int index = Integer.parseInt(entry.getKey().substring(1)) - 1;

            while(parts.size() <= index) {
                parts.add(new PointPart());
            }

            PointPart part = parts.get(index);
            switch(entry.getKey().charAt(0)) {
            case 'c':
                part.c = entry.getValue().getAsBoolean();
                break;
            case 'k':
                part.k = entry.getValue().getAsInt();
                break;
            }
        }

        return new Point(parts);
    }
}

class Point {
    List<PointPart> parts;

    Point(List<PointPart> parts) {
        this.parts = parts;
    }
}

class PointPart {
    boolean c;
    int k;
}
Russell Zahniser
  • 16,188
  • 39
  • 30
  • Thanks. If a field exists in the json but not in the class, what happens? – D-Klotz Mar 23 '12 at 18:29
  • I believe it normally throws a parse exception. You could make it ignore those fields by creating an `ExclusionStrategy` that excludes any field that does not appear in the target class. (But a custom deserializer is a better option) – Russell Zahniser Mar 23 '12 at 18:37
  • I am hoping to avoid writing a custom deserializer, since I'm catching this fairly early in the development cycle. I am going to scratch my head some more on this. Thanks for the advice. – D-Klotz Mar 23 '12 at 18:48
  • Custom deserializers are not really that much trouble - see example above. – Russell Zahniser Mar 23 '12 at 19:13
  • I thank you for the example. I guess what I am trying to avoid is having anything that may need to be modified if and when the backend wishes to add more functionality / data. If I can get them in the right mindset to begin with, then I won't have to touch or create code like this. From what I've gathered, I am going to recommend that 'point' be changed from an object to an array of string key value pairs. I should be able to make generic code that uses the type specified in the 'metadata' to create the correct type of objects in the tablemodel. Thanks again. – D-Klotz Mar 26 '12 at 14:53
2

I'd be more concerned with what looks like metadata fields in the data stream. The top level 'abort' and 'more' attributes look like some kind of structured string which you may have to parse? Aside from that, you just need to model each Java object with the widest possible set of fields that will be sent from your external program. You don't have to worry if transmitted data has one or more of the fields missing, most JSON libraries will just deserialize a null in that case. Also, most of the JSON libraries will also allow you to specify that you want to ignore unknown incoming fields.

So between deserializing null for missing fields and ignoring extra fields, you should be good to go for your parse.

Perception
  • 79,279
  • 19
  • 185
  • 195
  • So you are saying that extra fields in the json stream are ignored? I'm going to go out on a limb and guess that depends on the library? – D-Klotz Mar 23 '12 at 18:50
  • Yes, it depends on the library and more to the point, the configuration of said library. For example, this behavior is off by default in Jackson but can be turned on with a configuration setting. – Perception Mar 23 '12 at 19:02
  • I can't seem to up-vote or mark more than one as an answer. I'd mark this one as an answer as well. Thanks for the help. – D-Klotz Mar 26 '12 at 14:45
1

No this isn't particularly hard to deserialize into Java objects. JSON doesn't have many clues about typing information other than their primitives. Most of the concern about loosing type information can be recovered either by looking at the object you want to deserialize into, or have the user provide the object they want to deserialize into. That's precisely how flexjson works, and you have a lot of flexibility to define at each point how to deserialize it. It comes with sensible defaults for most problems, but you can always attach an ObjectFactory to specific class or path with in the JSON stream.

http://flexjson.sourceforge.net

chubbsondubs
  • 37,646
  • 24
  • 106
  • 138