2013-06-13 13 views
13

Ich habe Json Objekt mit arbitrary Werte im Inneren. Und ich möchte es in einer Karte deserialisieren. Alles ist in Ordnung, außer das Umwandeln von Ganzzahlen in Doppel. Siehe Beispiel:Gson. Deserialize Ganzzahlen als Ganzzahlen und nicht als Doppel

{"id":1, "inner_obj":{"key":"value","num":666,"map":{"key":"value"}}} 

deserialisiert dazu (map.toString()):

{id=1.0, inner_obj={key=value, num=666.0, map={key=value}}} 

Gibt es eine einfache Möglichkeit, "id" und "num" als Ganze Zahlen und nicht als Double deserialisieren?

+0

Siehe auch [diese Antwort] (http://Stackoverflow.com/a/43346123/179864) Ich schrieb für eine ähnliche Frage; Der Haken dabei ist, dass Sie die Daten als "Objekt" analysieren und dann auf das übertragen müssen, was Sie brauchen. – aditsu

+0

Jackson ist viel besser darin, Typen für Karten zu erkennen, und es ist viel einfacher zu konfigurieren als GSON: https://github.com/FasterXML/jackson-databind –

+0

Die Frage explizit nach Gson-Implementierung gefragt. Bitte bleiben Sie bei der Frage. – Megakoresh

Antwort

7

In JSON gibt es keinen Integer-Typ. 1 und 1.0 sind gleich. Sie müssen diese 1.0 zu 1 in Ihrem Code analysieren. Oder Sie müssen den JSON einer VO-Klasse zuordnen und den Typ der Felder der Klasse explizit definieren, damit GSON verstehen kann, wonach Sie suchen.

+4

Aber in Java gibt es Integers und Doubles. Und wenn ich eine Klasse mit dem Integer-Feld "num" habe, ist es einfach, {"num": 1} zu Integer zu deserialisieren, aber wenn gson JSON zu Map deserialisiert, möchte es Double verwenden. Also suche ich nach dem Weg, diesen Ansatz zu deaktivieren. – Moses

+0

@Moses definieren eine Map

+3

Die Map sollte nicht nur ganze Zahlen enthalten. – Moses

2

JSON hat nur einen einzigen Zahlentyp und es gibt keine Möglichkeit für den Parser, automatisch zu bestimmen, in welchen Typ er konvertiert werden soll.

Wenn Sie nicht eine stark typisierte Objektgraphen verwenden gehen, sollten Sie die JsonElement Typen verwenden:

JsonObject root = new Gson().fromJson(json, JsonObject.class); 
int num = root.getAsJsonObject("inner_obj").get("num").getAsInt(); 
1

Es ist mein Code

private static class MapDeserializer implements JsonDeserializer<Map<String,Object>> { 
    public Map<String,Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 
     Map<String,Object> m = new LinkedHashMap<String, Object>(); 
     JsonObject jo = json.getAsJsonObject(); 
     for (Entry<String, JsonElement> mx : jo.entrySet()){ 
      String key = mx.getKey(); 
      JsonElement v = mx.getValue(); 
      if(v.isJsonArray()){ 
       m.put(key, g.fromJson(v, List.class)); 
      }else if(v.isJsonPrimitive()){ 
       Number num = null; 
        try { 
         num = NumberFormat.getInstance().parse(v.getAsString()); 
        } catch (Exception e) { 
         e.printStackTrace(); 
        } 
       m.put(key,num); 
      }else if(v.isJsonObject()){ 
       m.put(key,g.fromJson(v, Map.class));  
      } 

     } 
     return m; 
    } 
} 

private static class ListDeserializer implements JsonDeserializer<List<Object>> { 
    public List<Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 
     List<Object> m = new ArrayList<Object>(); 
     JsonArray arr = json.getAsJsonArray(); 
     for (JsonElement jsonElement : arr) { 
      if(jsonElement.isJsonObject()){ 
       m.add(g.fromJson(jsonElement, Map.class)); 
      }else if(jsonElement.isJsonArray()){ 
       m.add(g.fromJson(jsonElement, List.class)); 
      }else if(jsonElement.isJsonPrimitive()){ 
       Number num = null; 
       try { 
        num = NumberFormat.getInstance().parse(jsonElement.getAsString()); 
       } catch (Exception e) { 
       } 
       m.add(num); 
      } 
     } 
     return m; 
    } 
} 

private static Gson g = new GsonBuilder().registerTypeAdapter(Map.class, new MapDeserializer()).registerTypeAdapter(List.class, new ListDeserializer()).setDateFormat("yyyy-MM-dd HH:mm:ss").serializeNulls().create(); 
1

für eine Lösung des verschachtelten Karte suchen Problem selbst und "종은" oben war die erste Antwort, die in den nicht trivialen Anwendungsfällen tatsächlich geholfen hat.

Da die Lösung über nur behandelt Nummer ich die Lösung aktualisiert generische Parsingfähigkeit bieten für Streich- und booleans auch unter den aktualisierten Code sehen:

private static class MapDeserializer implements JsonDeserializer<Map<String, Object>> { 

    public Map<String, Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 
     Map<String, Object> m = new LinkedHashMap<String, Object>(); 
     JsonObject jo = json.getAsJsonObject(); 
     for (Entry<String, JsonElement> mx : jo.entrySet()) { 
      String key = mx.getKey(); 
      JsonElement v = mx.getValue(); 
      if (v.isJsonArray()) { 
       m.put(key, g.fromJson(v, List.class)); 
      } else if (v.isJsonPrimitive()) { 
       Number num = null; 
       ParsePosition position=new ParsePosition(0); 
       String vString=v.getAsString(); 
       try { 
        num = NumberFormat.getInstance(Locale.ROOT).parse(vString,position); 
       } catch (Exception e) { 
       } 
       //Check if the position corresponds to the length of the string 
       if(position.getErrorIndex() < 0 && vString.length() == position.getIndex()) { 
        if (num != null) { 
        m.put(key, num); 
        continue; 
        } 
       } 
       JsonPrimitive prim = v.getAsJsonPrimitive(); 
       if (prim.isBoolean()) { 
        m.put(key, prim.getAsBoolean()); 
       } else if (prim.isString()) { 
        m.put(key, prim.getAsString()); 
       } else { 
        m.put(key, null); 
       } 

      } else if (v.isJsonObject()) { 
       m.put(key, g.fromJson(v, Map.class)); 
      } 

     } 
     return m; 
    } 
} 

private static class ListDeserializer implements JsonDeserializer<List<Object>> { 

    public List<Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 
     List<Object> m = new ArrayList<Object>(); 
     JsonArray arr = json.getAsJsonArray(); 
     for (JsonElement jsonElement : arr) { 
      if (jsonElement.isJsonObject()) { 
       m.add(g.fromJson(jsonElement, Map.class)); 
      } else if (jsonElement.isJsonArray()) { 
       m.add(g.fromJson(jsonElement, List.class)); 
      } else if (jsonElement.isJsonPrimitive()) { 
       Number num = null; 
       try { 
        num = NumberFormat.getInstance().parse(jsonElement.getAsString()); 
       } catch (Exception e) { 
       } 
       if (num != null) { 
        m.add(num); 
        continue; 
       } 
       JsonPrimitive prim = jsonElement.getAsJsonPrimitive(); 
       if (prim.isBoolean()) { 
        m.add(prim.getAsBoolean()); 
       } else if (prim.isString()) { 
        m.add(prim.getAsString()); 
       } else { 
        m.add(null); 
       } 
      } 
     } 
     return m; 
    } 
} 

private static Gson g = new GsonBuilder().registerTypeAdapter(Map.class, new MapDeserializer()).registerTypeAdapter(List.class, new ListDeserializer()).setDateFormat("yyyy-MM-dd HH:mm:ss").create(); 
+0

Das hat mir sehr geholfen! Ich möchte nur auf einen kleinen Fehler hinweisen. Im ListDeserializer sollten Sie "m.add (num)" im Block try {} entfernen, da er nach dem try/catch trotzdem hinzugefügt wird. Wenn Sie den Code unverändert verwenden, wird jede Zahl zweimal dem Ergebnis hinzugefügt. – peanutman

+0

Danke haben das Beispiel jetzt aktualisiert. –

+0

Ich denke, Sie können die Aufrufe von g.fromJson (...) zu context.deserialize (...) ersetzen, um die Notwendigkeit einer statischen Instanz von gson zu vermeiden (zumindest funktioniert es für mich für gson 2.6.2) – David

0

Hier ist mein Beispiel ist der erste Teil der Definition der Klasse, die ein int-Typ-Feld hat.

import com.google.api.client.util.Key; 

public class Folder { 

    public static final String FIELD_NAME_CHILD_COUNT = "childCount"; 

    @Key(FIELD_NAME_CHILD_COUNT) 
    public final int childCount; 

    public Folder(int aChildCount) { 
     childCount = aChildCount; 
    } 
} 

Dann der TypeAdapter, um den Zahlentyp in Gson in ein Java-Objekt zu konvertieren.

GsonBuilder gsb = new GsonBuilder(); 

gsb.registerTypeAdapter(Folder.class, new JsonDeserializer<Folder>() { 

      @Override 
      public Folder deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 

       int value = json.getAsJsonObject().get("childCount").getAsJsonPrimitive().getAsInt(); 

       return new Folder(value); 

      } 
     } 
); 

Der dritte Teil ist die Testdaten, und es funktioniert.

String gsonData = new String("\"folder\":{\"childCount\":0}"); 
0

möglich Classcast zu vermeiden, ist es besser zu Number zuerst zu werfen. Im folgenden Code wurde map aus JSON als Map ohne Generics deserialisiert.

int numberOfPages = ((Number) map.get("number_of_pages")).intValue(); 
Verwandte Themen