1

I'm having a little difficulty serializing some Json into a Java object. As you can see, the Json bit contains the top-level element args with a list of objects that contain a String as keys and mixed bag of values. I tried some creative things in my Java source file:

Json

{
    "args": {
        "all": {
            "expectCliArgs": true
        },
        "Prog3a": {
            "expectedNumberOfCliArgs": 2,
            "expectedErrorMessage": "Usage: java Prog3a infilename searchword"
        },
        "Prog3b": {
            "expectedNumberOfCliArgs": 2,
            "expectedErrorMessage": "Usage: java Prog3b infileName outfileName"
        },
        "Prog3c": {
            "expectedNumberOfCliArgs": 1,
            "expectedErrorMessage": "Usage: java Prog3c infileName"
        }
    }
}

Java

protected class Args {
    public Map<String, Set<Map<String, Object>>> map = new HashMap<>(); // no dice
}

protected class Args {
    public Map<String, Set<Map<String, ?>>> map = new HashMap<>(); // no dice
}

Is it possible that I need a custom serializer?

Update I tried doing the following in my class (per SO answer):

private Map<String, Set<ExpectedArgs>> args = new HashMap<>();

protected class ExpectedArgs {
    public Boolean expectCliArgs;
    public Integer expectedNumberOfCliArgs;
    public String expectedArgsErrorMessage;
}

but now it appears as though Gson is complaining about the syntax of my Json file:

Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 847
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:176)
    at com.google.gson.Gson.fromJson(Gson.java:803)
    at com.google.gson.Gson.fromJson(Gson.java:768)
    at com.google.gson.Gson.fromJson(Gson.java:717)
    at com.google.gson.Gson.fromJson(Gson.java:689)
    at javaGrader.config.ParseConfig.convertJsonToObject(ParseConfig.java:72)
    at javaGrader.config.ParseConfig.parseConfig(ParseConfig.java:34)
    at javaGrader.JavaGrader.main(JavaGrader.java:60)
Disconnected from the target VM, address: '127.0.0.1:62344', transport: 'socket'
Caused by: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 847
    at com.google.gson.stream.JsonReader.beginArray(JsonReader.java:338)
    at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:79)
    at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:60)
    at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:40)
    at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:187)
    at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:145)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:93)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:172)
    ... 7 more

I set some break points in my Json file and it looks like the "args" object is the one causing the heartburn. For reference, here's the full Json file, java Config class, and the class where Gson runs. It looks like Gson is confusing my object with an Array.

Col 847 is:

searchword"},"Prog3b":{
             ^

Any ideas?

Gson error

Update 2 My signature for the variable was incorrect:

private Map<String, Set<ExpectedArgs>> args = new HashMap<>();  // wrong
private Map<String, ExpectedArgs> args = new HashMap<>(); // right

1 Answer 1

2

I would define a class for the inner objects that contains both sets of fields, like this:

public static class ExpectedArgs {
    public Boolean expectCliArgs;
    public Integer expectedNumberOfCliArgs;
    public String expectedErrorMessage;
}

(Note I have deliberate used Boolean and Integer instead of boolean and int here, such that you can tell whether the field was set by testing for null.)

Then you can define the outer class like this:

public static class Data {
    public Map<String, ExpectedArgs> args;
}

Finally you can deserialize the JSON into an instance of Data:

String json = "...";

Gson gson = new Gson();

Data data = gson.fromJson(json, Data.class);

data.args.forEach((key, value) -> {
    System.out.println(key);
    System.out.println(value.expectCliArgs);
    System.out.println(value.expectedNumberOfCliArgs);
    System.out.println(value.expectedErrorMessage);
    System.out.println();
});

This code outputs the following for your example JSON:

all
true
null
null

Prog3a
null
2
Usage: java Prog3a infilename searchword

Prog3b
null
2
Usage: java Prog3b infileName outfileName

Prog3c
null
1
Usage: java Prog3c infileName
Sign up to request clarification or add additional context in comments.

1 Comment

This is a great solution! I ended up doing this exact thing last night but I encountered an error with Gson. Any ideas?

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.