how to deserialize a json/gson that could be a str

2020-02-06 09:57发布

问题:

I have the following json

"notes": {"note": [
         {
             "content": "Having wisdom teeth removed.",
             "from": "employee"
         },
         {
             "content": "Get well soon",
             "from": "manager"
         }
     ]},

the issue is that the value coud also be

 "notes": "",

or

"notes": {"note": {
            "content": "This is a test note.",
            "from": "employee"
        }},

and storing it in these

public  class Notes
{
    @SerializedName ("note")
    public List<Note> note;
}
public  class Note
{
    @SerializedName ("content")
    public String content;
    @SerializedName ("from")
    public String from;
}

I believe I solved the issue of not being an array but being an single object by doing this

public class Json {
    private static Gson gson;

    private static class MyNoteClassTypeAdapter implements JsonDeserializer<List<RequestsDTO.Note>> {
        public List<RequestsDTO.Note> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext ctx) {
            List<RequestsDTO.Note> vals = new ArrayList<RequestsDTO.Note>();
            if (json.isJsonArray()) {
                for (JsonElement e : json.getAsJsonArray()) {
                    vals.add((RequestsDTO.Note) ctx.deserialize(e, RequestsDTO.Note.class));
                }
            } else if (json.isJsonObject()) {
                vals.add((RequestsDTO.Note) ctx.deserialize(json,RequestsDTO.Note.class));
            } else {
                throw new RuntimeException("Unexpected JSON type: " + json.getClass());
            }
            return vals;
        }
    }

    public static Gson getGson()
    {
        if (gson == null)
        {
            Type ListType = new TypeToken<List<RequestsDTO.Note>>() {}.getType();
            GsonBuilder builder = new GsonBuilder();
            builder.registerTypeAdapter(DateTime.class, new DateTimeSerializer());
            builder.registerTypeAdapter(ListType, new MyNoteClassTypeAdapter());
            gson = builder.create();
        }
        return gson;
    }
}

And now I am stuck on when the whole thing just comes back as a string....

回答1:

The idea is try to get "note" field (from "notes" JSONObject) as JSONArray first and if it throws exception that will mean that there is no "note" JSONArray into "notes" JSONObject and that will mean that "note" is JSONObject. The same way we can figure out situation when note field is String.

try {
        //String jsonString="{\"notes\": {\"note\": [{\"content\": \"Having wisdom teeth removed.\",\"from\": \"employee\" }, {\"content\": \"Get well soon\", \"from\": \"manager\"} ] }}";
        //String jsonString="{\"notes\": { \"note\": {\"content\": \"This is a test note.\",\"from\": \"employee\"}}}";
        String jsonString="{\"notes\": { \"note\": \"\"}}";

        JSONObject jsonObject=new JSONObject(jsonString);
        JSONObject jsonObjectNotes=jsonObject.getJSONObject("notes");

        try{
            JSONArray jsonArrayNote=jsonObjectNotes.getJSONArray("note");
            for (int i = 0; i < jsonArrayNote.length(); i++) {

                JSONObject jsonObject2= jsonArrayNote.getJSONObject(i);
                String stringContent=jsonObject2.getString( "content");
                String stringFrom= jsonObject2.getString( "from");

                Log.e(getClass().getName(), "content="+stringContent +"; from="+stringFrom);
            }
        }
        catch(JSONException e){
            //that means that jsonObjectNotes has no jsonArray with name "notes" and "notes" is jsonObject
            try{
                JSONObject jsonObject3=jsonObjectNotes.getJSONObject("note");

                String stringContent=(String) jsonObject3.get( "content");
                String stringFrom=(String) jsonObject3.get( "from");

                Log.e(getClass().getName(), "content="+stringContent +"; from="+stringFrom);
            }
            catch(JSONException ex){
                //that means that jsonObjectNotes has no jsonObject with name "notes" and "notes" is empty String
                String stringNote=jsonObjectNotes.getString("note") ;       
                Log.e(getClass().getName(), "note is string ="+ stringNote);
            }
        }

    } catch (JSONException e) {
        e.printStackTrace();
    }

In my example code another get operations can also throw jsonExceptions but I think you get the idea.



回答2:

Refer the code snippet below to deserialize your json using Gson library without exceptions.

String jsonStr = "your json string ";

Gson gson = new Gson();
JsonObject jsonObj = gson.fromJson (jsonStr, JsonElement.class).getAsJsonObject();

JsonElement elem = jsonObj.get("note");

if(elem.isJsonArray()) { //**Array**
    List<Note> notelist = gson.fromJson(elem.toString(), new TypeToken<List<Note>>(){}.getType());
} else if(elem.isJsonObject()) { //**Object**
    Note note = gson.fromJson(elem.toString(), Note.class);
} else {  //**String**
    String note = elem.toString();
}


回答3:

Have a look at Genson library http://code.google.com/p/genson/. If your classes are inner classes make them static. The following code should solve your problem.

Genson genson = new Genson.Builder().withDeserializerFactory(new NotesDeserializerFactory()).create();
Notes notes = genson.deserialize(in, Notes.class);

// Define a factory so you can delegate the deserialization to existing mechanisms for lists and beans
class NotesDeserializerFactory implements Factory<Deserializer<Notes>> {

    @Override
    public Deserializer<Notes> create(Type type, Genson genson) {
        Converter<List<Note>> noteListConverter = genson.provideConverter(new GenericType<List<Note>>() {}.getType());
        Converter<Note> noteConverter = genson.provideConverter(Note.class);
        return new NotesDeserializer(noteListConverter, noteConverter);
    }

}

// define an implementation for you Notes class so you can handle the different cases
class NotesDeserializer implements Deserializer<Notes> {
    private final Converter<List<Note>> noteListConverter;
    private final Converter<Note> noteConverter;

    public NotesDeserializer(Converter<List<Note>> noteListConverter,
            Converter<Note> noteConverter) {
        this.noteListConverter = noteListConverter;
        this.noteConverter = noteConverter;
    }

    @Override
    public Notes deserialize(ObjectReader reader, Context ctx) throws TransformationException,
            IOException {
        Notes notes = new Notes();
        if (reader.getValueType() == ValueType.ARRAY) notes.note = noteListConverter.deserialize(reader, ctx);
        else if (reader.getValueType() == ValueType.OBJECT) notes.note = Arrays.asList(noteConverter.deserialize(reader, ctx));
        else { // it is a litteral (string, numeric, boolean, null)
            notes.note = new ArrayList<Note>();
        }
        return notes;
    }
}