4

I have JSON that looks like this:

{
  "values": [
    [
      123456,
      789.0
    ],
    [
      123457,
      null
    ]
  ]
}

The "schema" is: each value is an array of exactly two things, the first being a long and the second being a double (or null). I'd like to parse this into a Java object (just a POJO).

I tried Jackson, but it has a known bug which prevents nulls in arrays from working: https://github.com/FasterXML/jackson-databind/issues/403

I also tried Gson, but it apparently cannot cope with the idea of transforming arrays into Java objects (rather than into Java arrays):

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 3 column 6

Here's a full test class that demonstrates the non-working of both Jackson and Gson for this simple task:

import java.util.List;

import org.codehaus.jackson.annotate.JsonCreator;
import org.codehaus.jackson.map.ObjectMapper;

import com.google.gson.Gson;

public class JsonTest {

    private static final String TEST_JSON = 
                    "{\n" +
                    "  \"values\": [\n" +
                    "    [\n" +
                    "      123456,\n" +
                    "      789.0\n" +
                    "    ],\n" +
                    "    [\n" +
                    "      123457,\n" +
                    "      null\n" +
                    "    ]\n" +
                    "  ]\n" +
                    "}\n";

    public static class MyPojo1 {
        public List<TimestampAndValue> values;

        public static class TimestampAndValue {
            public long timestamp;
            public Double value;
            @JsonCreator
            public TimestampAndValue(List<Number> nums) {
                if(nums == null || nums.size() < 2) {
                    throw new IllegalArgumentException(String.format("Expected at least two numbers (timestamp & value), instead got: %s", nums));
                }
                this.timestamp = nums.get(0).longValue();
                this.value = nums.get(1).doubleValue();
            }
        }
    }

    public static class MyPojo2 {
        public List<TimestampAndValue> values;

        public static class TimestampAndValue {
            public long timestamp;
            public Double value;
        }
    }

    public static void main(String[] args) {
        try {
            System.out.println(new ObjectMapper().readValue(TEST_JSON, MyPojo1.class));
        } catch(Throwable t) {
            t.printStackTrace();
        }
        try {
            System.out.println(new Gson().fromJson(TEST_JSON, MyPojo2.class));
        } catch(Throwable t) {
            t.printStackTrace();
        }
    }

}

Question: what's the proper way to parse this JSON into a Java object? I'm thinking of just using the org.json raw materials to build my own simple parser, since the handy libraries are not so handy.

2
  • I think, you should remove \n's from TEST_JSON as it would improve readability and would not affect behavior. Commented Aug 8, 2014 at 11:07
  • Actually it would reduce readability as then the libraries' failure messages would be unable to indicate which line the error occurred on. :) Commented Aug 11, 2014 at 12:35

3 Answers 3

4

Here is a self-contained, simplified example on how to do this with Jackson:

// my pojo
public class Main {
    // my property - it's a 2-dim array (might work with a List of Lists as well)
    @JsonProperty(value = "values")
    Double[][] values;
    public static void main(String[] args) throws Exception {
        ObjectMapper om = new ObjectMapper();
        String json = "{\"values\":[[123456,789.0],[123457,null]]}";
        Main m = om.readValue(json, Main.class);
        // prints the de-serialized json
        System.out.println(Arrays.deepToString(m.values));
    }
}

Output

[[123456.0, 789.0], [123457.0, null]]

Notes

  • I am using Doubles instead of primitives, so they are nullable and no additional setting needs to be used with the ObjectMapper.
  • You seem to be using List<Number> but what you have in your JSON looks more like a 2-dimensional array (or a List of Lists).
  • I am using the LATEST (as in Maven LATEST) version of the Jackson libs. The latest stable version is 2.3.2 at the moment, but a Maven search will return latest version as 2.4.1.3 - see Alexey's comment.
  • See Jackson homepage for details.
Sign up to request clarification or add additional context in comments.

2 Comments

note that the latest Jackson databind version is 2.4.1.3. search.maven.org/#search%7Cga%7C1%7Cjackson-core
@AlexeyGavrilov thanks I must have confused with latest stable as advertised on their website.
0

If you want to use GSON than you need following changes :

MyPojo2 class :

public class MyPojo2 
{
    public List<List<Double>> values;

    public List<List<Double>> getValues() {
        return values;
    }

    public void setValues(List<List<Double>> values) {
        this.values = values;
    }
}  

Your JSON contain List of List, you need same data type for class object values.

Test class

public static void main (String[] args)
    {
        try 
        {
            String json = "{\"values\":[[123456,789],[123457,null]]}";
            MyPojo2 obj = null;

            Gson gson = new Gson();
            obj = gson.fromJson(json, MyPojo2.class);
            System.out.println(obj);
            System.out.println(gson.toJson(obj));
        } 
        catch (Exception e) 
        {
            e.printStackTrace();
        }
    }

Sample Output :

MyPojo2@b09697
{"values":[[123456.0,789.0],[123457.0,null]]}

Comments

0

It seems I was trying to do a bit too much with my data types and the libraries (or at least these two) don't really support it.

The input JSON is, in this case, known to have a regular structure: "values" is an array which contains subarrays, each of which is always of size 2, such that the first item is a long and the second is a double (or null). So I was hoping I could just directly translate this into a suitable POJO. The idea is that if somehow an array somehow has x != 2 elements, or if the elements are not a long and a (nullable) double, that's a parse error; otherwise, it gets automatically translated into a POJO.

Instead, what I went for was manual parsing of the array using the standard JSON Java library. I'm doing the validity checks manually as I go, accumulating the values into a List, and returning that. As a bonus the fields in my POJO can now easily be public final which is nice. One could also use Jackson or Gson do convert the JSON into primitives (see the other answers) but then you'd still have to check those primitives manually which seems to kind of defeat the point of using a fancy library.

I still think it would be nicer to be able to use a library to do this conversion, but not sure if there is a library that supports it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.