简体   繁体   中英

Java JSON Deserialize when type is String or List<String>

Is there a simple way to deserialize a value that may be either a String or a List<String> ?

I have to process a large JSON that I'll abbreviate like:

{"A": {"item1": 1,
        ...
       "itemN": "SomeString",
        ...
       "itemM": 123.45},
 "B": { ... },
 "C": { ... },
 }

And sometimes it looks like this:

{"A": {"item1": 1,
        ...
       "itemN": ["SomeString1", "SomeString2"],
        ...
       "itemM": 123.45},
 "B": { ... },
 "C": { ... },
 }

I deserialize with:

MyData data = new Gson().fromJson(rxJSON, DataClass.class);

Where DataClass:

@Parcel
public class DataClass {
    @SerializedName("A")
    private AClass groupA;
    @SerializedName("B")
    private BClass groupB;
    @SerializedName("C")
    private BClass groupC;
    ... getters/setters ...
}

and AClass:

@Parcel
public class AClass {
    @SerializedName("item1")
    private int item1;
    ...
    @SerializedName("itemN")
    private List<String> itemN;
    ...
    @SerializedName("itemM")
    private float itemM;
    ... getters/setters ...
}

Ideally, I'd like to use AClass for both JSONs, and in the case where itemN is a String , simply treat it as if it were a single element List<String> (ie. treat "SomeString" as ["SomeString"] ). After deserializing, I always want to access it as a list for simplicity in the rest of my code.

I've seen suggestions for Polymophism solutions and solutions suggesting attempting to deserialize with one version of a class assuming one type (such as String ) and in an exception catch deserialize with a version of the class assuming the other type (such as List<String> ). Other solutions suggest a more manual/piece-wise deserialization where it would deserialize only one level of the JSON hierarchy at a time until I came to itemN and then check it's type. It seems like there should be a simpler way. Is there?

I found a good solution thanks to: https://stackoverflow.com/a/31563539/3571110 and https://stackoverflow.com/a/6205384/3571110

The first link shows using a custom deserializer for the entire JSON (too much work for me), and within that manages String to List<String> . The second link gave me the insight that I could make a custom deserializer for basic types like List<> (I previously thought I could only use custom classes). Combining those ideas and making the appropriate changes yields (everything else staying the same):

Solution:

public class ListOrStringDeserializer implements JsonDeserializer<List<String>> {

    @Override
    public List<String> deserialize(JsonElement json, Type typeOfT,
                                    JsonDeserializationContext context) 
            throws JsonParseException {

        List<String> items = Collections.emptyList();
        if (json.isJsonArray()) {
            items = context.deserialize(json, List.class);
        } else if (json.isJsonPrimitive()) {
            items = Collections.singletonList((String) context.deserialize(json, String.class));
        }
        return items;
    }
}

Then register before deserializing:

Gson gson = new GsonBuilder().registerTypeAdapter(new TypeToken<List<String>>() {}.getType(), new ListOrStringDeserializer()).create();
MyData data = gson.fromJson(rxJSON, DataClass.class);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM