2015-01-25 7 views
6

Ich versuche, ein JSON-Array mit GSON deserialisieren. Alle meine verschachtelten Objekte sind in einem "eingebetteten" Objekt eingebettet.GSON: Entfernen Sie unnötiges übergeordnetes Objekt beim Deserialisieren

{ 
    "Book": { 
     "name": "Book 1", 
     "published": 1999, 
     "links": { 
      "url": "www.book1.com" 
     }, 
     "embedded": { 
      "Author": { 
       "name": "John Doe", 
       "links": { 
        "url": "www.johndoe.com" 
       } 
      } 
     } 
    } 
} 

Ich könnte auch eine Situation wie diese haben:

{ 
    "Book": { 
     "name": "Book 1", 
     "published": 1999, 
     "links": { 
      "url": "www.book1.com" 
     }, 
     "embedded": { 
      "Publisher": { 
       "name": "Publishing Company", 
       "links": { 
        "url": "www.publishingcompany.com" 
       } 
      } 
     } 
    } 
} 

Dies ist ein äußerst einfaches Beispiel. Einige meiner Objekte können 2 oder 3 Ebenen tief verschachtelt sein, und alle sind in einem "eingebetteten" Objekt. Außerdem hat jedes Objekt eine verschachtelte "URL" in einem "Links" -Objekt. Ich habe ungefähr 20 verschiedene Modellobjekte, jedes mit mehreren Feldern, und jedes von ihnen hat das "eingebettete" Objekt. Ich fing an, für jedes Modell benutzerdefinierte Deserialisierer zu schreiben, aber das scheint den ganzen Punkt der Verwendung von gson zu verfehlen, und ich weiß vielleicht nicht immer, was das eingebettete Objekt ist.

Ich fand diese answer, aber es war für die Serialisierung von Objekten. Ich habe schon eine Weile versucht, das herauszufinden, und habe nichts gefunden, was funktioniert.

My Book Modell sieht wie folgt aus:

public class Book { 
    String name; 
    int published; 
    String url; 
    Author author; 
    Publisher publisher; 
} 

Autor Klasse:

public class Author { 
    String name; 
    String url; 
} 

Verlag Klasse:

public class Publisher { 
    String name; 
    String url; 
} 

Und hier ist mein Buch Deserializer so weit:

public class BookDeserializer implements JsonDeserializer<Book> { 
    @Override 
    public Book deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 

     final JsonObject jsonObject = json.getAsJsonObject(); 

     Book book = new Book(); 
     book.setName(jsonObject.get("name").getAsString()); 
     book.setPublished(jsonObject.get("published").getAsInt()); 
     String url = jsonObject.getAsJsonObject("links").get("url").getAsString(); 
     book.setUrl(url); 

     // 1) How to get rid of this and skip to the "real" nested object? 
     final JsonObject embeddedObject = jsonObject.getAsJsonObject("embedded"); 

     // 2) See what the "embedded" object actually is. 
     String embeddedModel; 
     Set<Map.Entry<String, JsonElement>> entrySet = embeddedObject.entrySet(); 
     for (Map.Entry<String, JsonElement> entry : entrySet) { 

      // Author or Publisher 
      embeddedModel = entry.getKey(); 
     } 

     // We have the model's key, now add code here to deserialize whatever the object is 

     return book; 
    } 
} 

Ich muss immer noch den JSON analysieren und jedes Feld für Buch festlegen. Dann müsste ich Code hinzufügen, um den korrekten Deserializer für das verschachtelte Objekt zu ermitteln und zu verwenden. Sieht so aus, als würde ich noch einen benutzerdefinierten Deserializer für jedes Objekt benötigen, um die "URL" zu erhalten. Ich bin ziemlich neu zu Gson, also vielleicht gibt es gerade etwas, das ich übersehe, aber es scheint, dass ich ebensogut manuell alle JSON analysieren und nicht einmal Gson verwenden könnte. Vielleicht gibt es eine Möglichkeit, Json zu verflachen?

Irgendwelche Ideen, wie dies zu analysieren und noch die Bequemlichkeit von Gson verwenden, oder ist das überhaupt möglich? Vielleicht könnte Jackson damit besser umgehen?

+0

Cant Gson deserialize und serialisieren dies standardmäßig? Warum musst du dein eigenes schreiben? – user489041

Antwort

1

Erstellen Sie eine Klasse namens eingebettet und es als ein Feld in Buch hinzuzufügen:

public class Book { 
    String name; 
    int published; 
    Embedded embedded; 
} 

Dann eine eingebettete Klasse erstellen:

public class Embedded { 
    Author Author; 
    Publisher Publisher; 
} 

Sie Ihre Klassen nach dem JSON Modell

+0

So dachte ich zuerst, aber ich hoffte, dass es eine "elegantere" Lösung gab. Es scheint eine Verschwendung zu sein, Links und eingebettete Klassen zu erstellen (es wird noch viele mehr geben), jede mit Objekten, die ich vielleicht gar nicht verwende. Ich denke, es ist einfacher als benutzerdefinierte Deserializer für jeden zu erstellen. – Mark

+0

verwenden wir [Dozer] (http://dozer.sourceforge.net/) in unserer Firma, um zwischen diesen Klassen zu mappen. eigentlich machen wir ein flaches Antwortobjekt, das wir senden wollen, und benutzen den Dozer, um sie abzubilden. es hilft beim automatischen Zuordnen der Beans zueinander – alizelzele

1

Mein Der erste Gedanke war, das JSON zu parsen und es zu hacken, aber es sieht so aus, als wären GSON JsonObject s unveränderlich.

Ich würde daher einen einfachen Stream-Parser schreiben, der nach "embedded": { und "links": { sucht und sie entfernen. Führen Sie auch einen einfachen Klammerzähler aus, um die passende Klammer zu entfernen. Wenn es die Zeit erlaubt, könnte ich einen zusammenwerfen.

BTW - Ihr Beispiel JSON fehlt ein Komma - fügen Sie es here, um es zu überprüfen.

Hinzugefügt: - Der Stream-Parser geriet außer Kontrolle - obwohl es die sauberere Option gewesen wäre. Wenn Sie einen JSON-Stream-Parser wie SAX für XML finden können, können Sie es vielleicht besser so machen.

Der zweite Mechanismus geht davon aus, dass Sie die gesamte JSON-Datei in einen String im Speicher einfügen können. Nicht ideal, aber wahrscheinlich eine akzeptable Lösung für die meisten Setups. Dies verwendet dann einen einfachen Regex plus einen Klammerzähler, um die erforderlichen Teile zu entfernen.

/** 
* Finds the first matching close brace - assuming an open brace has just been removed from the `start` position. 
*/ 
private int closeBrace(StringBuilder s, int start) { 
    int count = 1; 
    boolean inQuotes = false; 
    for (int i = start; i < s.length(); i++) { 
     char ch = s.charAt(i); 
     // Special case escapes. 
     if (ch != '\\') { 
      switch (ch) { 
       case '"': 
        inQuotes = !inQuotes; 
        break; 
       case '{': 
        if (!inQuotes) { 
         count += 1; 
        } 
        break; 
       case '}': 
        if (!inQuotes) { 
         count -= 1; 
         if (count == 0) { 
          return i; 
         } 
        } 
        break; 
      } 
     } else { 
      // Escape character - skip the next character. 
      if (i < s.length()) { 
       i += 1; 
      } 
     } 
    } 
    // Failed to find 
    return s.length(); 
} 

/** 
* Removes the JSON specified. 
*/ 
private String hack(String json, String remove) { 
    // Transfer to an sb for slicing and dicing. 
    StringBuilder s = new StringBuilder(json); 
    // Build my pattern 
    Pattern p = Pattern.compile("\"" + remove + "\"\\s*:\\s*\\{"); 
    // Make my Matchjer. 
    Matcher m = p.matcher(s); 
    // Is it there? 
    while (m.find()) { 
     int start = m.start(); 
     int end = m.end(); 
     // Kill the match. 
     s.delete(start, end); 
     // Walk forward to find the close brace. 
     end = closeBrace(s, start); 
     // And remove it. 
     if (end < s.length()) { 
      s.delete(end, end + 1); 
     } 
     // Rebuild the matcher. 
     m = p.matcher(s); 
    } 
    return s.toString(); 
} 

private void test(String json) { 
    JsonParser parser = new JsonParser(); 
    JsonElement e = parser.parse(json); 
    System.out.println(e); 
} 

public void test() { 
    String json = "{'Book': {'name': 'Book \\'1\\'','published': 1999,'links': {'url': 'www.book1.com'},'embedded': {'Publisher': {'name': 'Publishing Company','links': {'url': 'www.publishingcompany.com'}}}}}".replace("'", "\""); 
    test(json); 
    json = hack(json, "embedded"); 
    test(json); 
    json = hack(json, "links"); 
    test(json); 
} 

druckt:

{"Book":{"name":"Book \"1\"","published":1999,"links":{"url":"www.book1.com"},"embedded":{"Publisher":{"name":"Publishing Company","links":{"url":"www.publishingcompany.com"}}}}} 
{"Book":{"name":"Book \"1\"","published":1999,"links":{"url":"www.book1.com"},"Publisher":{"name":"Publishing Company","links":{"url":"www.publishingcompany.com"}}}} 
{"Book":{"name":"Book \"1\"","published":1999,"url":"www.book1.com","Publisher":{"name":"Publishing Company","url":"www.publishingcompany.com"}}} 

, die wie etwas aussieht, was Sie suchen.

+0

Danke für die Hilfe und ich habe den JSON korrigiert. Das hört sich nach einer guten Idee an und ein Beispielcode wäre großartig! Gson unterstützt Streaming, meinst du das? – Mark

+0

@Mark - Siehe ** Hinzugefügt ** - Keine sehr schöne Lösung, aber es sollte für kleine bis mittlere JSON-Objekte funktionieren. – OldCurmudgeon

+0

Vielen Dank für die Bereitstellung des Codebeispiels. Ich werde jedoch nur die zusätzlichen Klassen erstellen, damit meine Java-Modelle mit dem JSON übereinstimmen. Es sieht so aus, als ob gson keine integrierte Möglichkeit bietet, ausgewählte verschachtelte Objekte auszupacken. – Mark

Verwandte Themen