4

How to deserialize a JSON String that contains lists of objects within other objects? I found explanations for simple deserialization, but I can't extrapolate much from them, as they're all a little bit off. As an example(POJOs ommited), for

String json = "[ {
  "id" : 33147,
  "name" : "Refinancing",
  "photos" : [ {
    "name" : "347.png",
    "url" : "/loans/568/photos/092"
  } ],
  "username" : "zach1985"
} , {
  "id" : 7693,
  "name" : "Stuff",
  "photos" : [ {
    "name" : "newpic1.png",
    "url" : "/loans/123446/photos/890"
  } ],
  "username" : "sengaia"
} ]";

ArrayList<Ad> ads = new ArrayList<>;

deserialize(json, ads);

System.out.println(ads.get(1).getName());
System.out.println(ads.get(0).getPhotos().get(0).getName());

The outputs would be "Stuff" and "347". How would then the deserialize() method need to be implemented?

Sargon1
  • 854
  • 5
  • 17
  • 48
  • You cant deserialize this to a arraylist. An arraylist only holds 1 key with no value. I suggest you use a HashMap. EDIT: Oops, I didnt see it was a JSON array. – Martacus Jun 02 '16 at 19:29
  • @Martacus It's a JSON array. – Sotirios Delimanolis Jun 02 '16 at 19:29
  • `new ObjectMapper().readValue(data, FullyTypedThing.class)` will do the job if the `FullyTypedThing` is exactly what it says on the tin: is composed of other fully typed classes or collections of fully typed classes. You can even call all of them `Embedded` and define as `public static` nested classes at the places where they will be used. Do you really need an example or will you manage to define a class yourself? – Oleg Sklyar Jun 02 '16 at 19:31
  • 1
    Q: How to deserialize a JSON String that contains lists of objects within other objects? A: There are lots of different ways. If you're planning on working with JSON in Java more extensively, I'd recommend familiarizing yourself with Jackson: http://www.mkyong.com/java/how-to-convert-java-object-to-from-json-jackson/ – paulsm4 Jun 02 '16 at 19:31
  • 1
    As others have indicated - learn a JSON framework like Jackson. Then use `ObjectMapper` to convert your JSON to objects. @Oleg indicated that you can use: `new ObjectMapper().readValue(data, FullyTypedThing.class)` - if you want to read an array, just pass in an array of that class to the same method, as in: `new ObjectMapper().readValue(data, FullyTypedThing[].class` – dutoitns Jun 02 '16 at 19:42
  • 1
    You might also need to configure the `ObjectMapper` depending on your requirements (Jackson has a few options). But I think just get started - and then try and tweak `ObjectMapper` if required by calling its `#configure(..)` and `#setVisibility(..)` methods. – dutoitns Jun 02 '16 at 19:45
  • @Oleg I'll try grafting the examples provided myself to my case first, thanks. – Sargon1 Jun 02 '16 at 19:49
  • I just put the runnable example in the answer. I contend it being duplicate as the original answer is fairly lame (I mean duplicate question and the answer to the original question)... – Oleg Sklyar Jun 02 '16 at 19:50

1 Answers1

2

As I indicated in the comment to the question here is the test that shows how to do this:

public class JSONTest {

  public static class FullyTypedThing {
    public int id;
    public String name;
    public List<Photos> photos = Lists.newArrayList();
    public String username;

    public static class Photos {
      public String name;
      public String url;
    }
  }

  private static final String json = "[ { \"id\" : 33147,    \"name\" : \"Refinancing\", \"photos\" : [ {"
    + "\"name\" : \"347.png\", \"url\" : \"/loans/568/photos/092\"  } ],"
    + "\"username\" : \"zach1985\"} , {  \"id\" : 7693,  \"name\" : \"Stuff\","
    + "\"photos\" : [ {  \"name\" : \"newpic1.png\",  \"url\" : \"/loans/123446/photos/890\"  } ],"
    + "\"username\" : \"sengaia\"  } ]";

  @Test
  public void roundtrip() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    List<FullyTypedThing> res = Lists.newArrayList(
      mapper.readValue(json, FullyTypedThing[].class));
    assertEquals(2, res.size());
    assertEquals(33147, res.get(0).id);
    assertEquals("Refinancing", res.get(0).name);
    assertEquals("347.png", res.get(0).photos.get(0).name);
    assertEquals("/loans/568/photos/092", res.get(0).photos.get(0).url);
    assertEquals(7693, res.get(1).id);
    assertEquals("Stuff", res.get(1).name);
    assertEquals("newpic1.png", res.get(1).photos.get(0).name);
    assertEquals("/loans/123446/photos/890", res.get(1).photos.get(0).url);

    assertEquals("[{\"id\":33147,\"name\":\"Refinancing\",\"photos\":"
      + "[{\"name\":\"347.png\",\"url\":\"/loans/568/photos/092\"}],"
      + "\"username\":\"zach1985\"},{\"id\":7693,\"name\":\"Stuff\","
      + "\"photos\":[{\"name\":\"newpic1.png\",\"url\":\"/loans/123446/photos/890\"}],"
      + "\"username\":\"sengaia\"}]", mapper.writeValueAsString(res));

  }
}
Oleg Sklyar
  • 9,834
  • 6
  • 39
  • 62
  • A few clarifications: 1) by "ObjectMapper", you mean "the Jackson library": https://github.com/FasterXML/jackson, 2) in your example, you use `mapper.readValue(..., FullyTypedThing.class)`. This is OK. But Jackson *ALSO* allows you to access JSON entities *WITHOUT* necessarily defining a corresponding Java class, if needed. – paulsm4 Jun 02 '16 at 20:33
  • 1
    Yes, I use Jackson, which is de-facto the standard. With respect to your second point, I fail to see the point. I know I can do that, so what? Why would I want untyped data and a lot of parsing code if I can have it properly typed and done in one line, transparent, clear and type-safe? Defining a holder class, like the one in this test, is a 5min work, even for more complex data structures. Yes, when working with heterogenous collections one might recourse to raw access, but then I would question the reasons behind that data being heterogeneous. More often than not it is poor design. – Oleg Sklyar Jun 02 '16 at 21:37