# Nested JSON in Java ansprechen [Wordpress Rest Api]



## chelayos (13. Sep 2016)

Pro JSONObjekt erstelle ich eine Map. Jedoch bei "replies" funktioniert dies nicht. Ich vermute, dies liegt daran, weil es ein JSONArray ist. Siehe dazu [Bild]: generiertes JSON von WP-API.


```
String url = "https://studyhard.tk/wp-json/wp/v2/posts/"+id+"?_embed=1";

        final StringRequest request = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
            @Override
            public void onResponse(String s) {
                gson = new Gson();

                mapPost = (Map<String, Object>) gson.fromJson(s, Map.class);
                mapTitle = (Map<String, Object>) mapPost.get("title");
                mapContent = (Map<String, Object>) mapPost.get("content");
                map_embedded = (Map<String, Object>) mapPost.get("_embedded");

                // Do we have replies?
                if(map_embedded.containsKey("replies"))
                {
                    map_replies = (Map<String, Object>) map_embedded.get("replies"); // Error
                }
```



Spoiler: Mit folgenden Datentypen habe ich bereits experimentiert & klappt nicht.



map_replies = Map<String, Object>
map_replies = Map<String, Array>
map_replies = Map<String, ArrayList>
map_replies = Map<String, ArrayList<Object>>
map_replies = Map<String, ArrayList<JSONObject>>
map_replies = Map<String, JSONArray>
map_replies = Map<String, List<Object>>
map_replies = Map<String, List<JSONObject>>
map_replies = Map<String, List<JSONArray>>


Ist dies überhaupt möglich mit Map? Oder kennt ihr einen besseren Datentyp, damit ich auf "replies" zugreifen kann?


----------



## mrBrown (13. Sep 2016)

Wenn du eh GSON nutzt, bau dir das JSON-Objekt als normales Java-Objekt nach.

also etwa:

```
class Post {//Namen und Typen etwas geraten...
int id;
LocalDateTimt date;
//...
Embedded _embedded;
}

class Embedded {//Namen und Typen etwas geraten...
List<Author> author;
List<Reply> replies;//wenn da Liste passt, uU List<List<...>>?
//...
}
```

Ansonsten, "replies" ist eine Liste, keine Map


----------



## chelayos (13. Sep 2016)

Das Problem ist, ich kann nur Klassen mit "Object" machen:


```
public class WPPost {
    public Object id;
    public Object title;
}
```

Wenn ich int id oder string title verwende stürzt es ab.

```
FATAL EXCEPTION: main
                                                                      Process: tk.studyhard.android, PID: 15861
                                                                      com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 11
```

Wenn ich nun den Titel als Object ausgebe

```
wp = (WPPost) gson.fromJson(s, WPPost.class);
content.loadData(wp.title.toString(),"text/html; charset=UTF-8", null);
```
kommt das:
{rendered=Willkommen bei Wordpress}
Aber ich benötige nur den Teil Willkommen bei Wordpress


----------



## chelayos (13. Sep 2016)

Kann ich die Werte ohne {rendered=} speichern? Wenn ja wie?

Der Code ist nun auf Github verfügbar.
https://github.com/Chelayos/WPAndroid


----------



## RalleYTN (13. Sep 2016)

Schonmal mit List<Object> oder Object[] versucht?


----------



## mrBrown (13. Sep 2016)

Oder das Objekt passend nachbauen. Titel zB ist kein String, sondern ein Objekt, welches du dann auch repräsentieren müsstest.


----------



## chelayos (13. Sep 2016)

Mit einer gewöhnlichen ArrayList (ohne Map) scheint es zu funktionieren.

```
// Do we have replies?
                if(map_embedded.containsKey("replies"))
                {
                    map_replies = (ArrayList) map_embedded.get("replies");
                    content.loadData(map_replies.toString(), "text/html; charset=UTF-8", null);
                }
```

Code wurde ebenfalls auf Github aktualisiert.

der Output:






Ich benötige folgende Felder:

content
author_name
date
Gibts da nen besseren Weg als einfach die ganze Arraylist abzusuchen?
Ich habe weiter oben im Code Maps verwendet, weil das beim Postcontent so einfach ging:
mapContent.get("rendered").toString(); 
So wird nur der Inhalt ohne das {rendered=} ausgegeben. Gibts sowas ähnlcihes auch bei Arraylist?

@RalleYTN , @mrBrown Danke für eure Mitarbeit!
@mrBrown wie meinst du ein Objekt nachbauen / repräsentieren? Sorry steh grad aufm Schlauch. Evtl Beispiel wäre gut^^


----------



## mrBrown (13. Sep 2016)

chelayos hat gesagt.:


> @mrBrown wie meinst du ein Objekt nachbauen / repräsentieren? Sorry steh grad aufm Schlauch. Evtl Beispiel wäre gut^^



So wie weiter oben von mir, um da was besseres zu bringen, bräuchte man einen passenderen Ausschnitt des Jsons 
Der Weg mit der Map geht halt auch, aber grad wenn man GSON schon nutzt, find ich den Weg mit Objekten schöner...


----------



## chelayos (14. Sep 2016)

Habe nun deinen Rat befolgt und habe Klassen erstellt:


```
public class WPPost {
    Map<String, Object> title;
    Map<String, Object> content;
    Embedded _embedded;
}
```


```
public class Embedded
{
    //Object[] replies;
//Object replies
    //ArrayList<Reply>[] replies;
//Arraylist<Reply> replies;
//List<Reply> replies;
    List<Reply>[] replies;
}
```


```
public class Reply
//Map<String, Object> author_name;
    //Map<String, Object> date;
    //Map<String, Object> content;
    String author_name;
    String date;
    String content;
```

Im Code kann ich nun bis zu _embedded zugreifen. Aber ich werde aber nicht schlau draus, wie es nun weiter gehen soll. Klar ich muss ich replies als Klasse nachbauen aber das funktioniert bis jetzt noch nicht. Habe verschiedene Dateitypen versucht (auch mit []) aber ich krieg nur Fehler oder Count: 0, wenn ich items zähle.


----------



## mrBrown (14. Sep 2016)

Dann zeig doch mal die Fehlermeldung, die du bei welchem DatenTyp bekommst.

Entweder List<Reply> oder List<List<Reply>> müsste passen (sieht zumindest in dem Screenshot so aus...)


----------



## chelayos (14. Sep 2016)

List hat funktioniert! 
http://studyhard.tk/wp-content/uploads/2016/09/replies.png

Aber irgendwas fehlt noch...
Wie kann ich nun auf das Objekt zugreifen?

Reply r = (Reply)wp._embedded.replies[0];
content += r.author_name;
java.lang.ClassCastException: java.util.ArrayList cannot be cast to com.wordpressappexample.Reply


----------



## mrBrown (14. Sep 2016)

Guck dir einfach mal die Rückgagetypen bzw die Objekte selbst an, daraus und aus der Exception sollte das eig klar werden 

Generell, wenn du bei sowas casten musst ist was falsch gelaufen


----------



## chelayos (15. Sep 2016)

Bin nun soweit, dass Kommentare dargestellt werden *Freudensprung*





Ich habe das Datum auf das deutsche Format dd.MM.yy umgewandelt. Sieht mir persönlich aber zu kompliziert aus. Macht man das so in Java oder geht das doch einfacher?  


```
String content = "";

                SimpleDateFormat formatter1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
                SimpleDateFormat formatter2 = new SimpleDateFormat("dd.MM.yy");

                for (Replies r : wp._embedded.replies[0])
                {
                    try
                    {
                        content += formatter2.format(formatter1.parse(r.date));
                    }
                    catch(ParseException e)
                    {
                        //invalid date
                    }
                    content += " " + r.author_name + ": ";
                    content += r.content.get("rendered");
                }
                content= content.replaceAll("<p>", "");
```


----------



## VfL_Freak (15. Sep 2016)

Moin,


chelayos hat gesagt.:


> Ich habe das Datum auf das deutsche Format dd.MM.yy umgewandelt. Sieht mir persönlich aber zu kompliziert aus


Wieso kompliziert ??



chelayos hat gesagt.:


> Macht man das so in Java oder geht das doch einfacher?


Was genau ist bei *r.date* das _date_??
Nutzt Du Java8 oder ist hier noch das als _*depricated *_gekennzeichnete _date_ der älteren Version im Einsatz?

Aber setz' dich ggf. mal mit der LIB JodaTime auseinander, die ist deutlich komfortabler !
http://www.joda.org/joda-time/

Gruß Klaus


----------



## mrBrown (15. Sep 2016)

VfL_Freak hat gesagt.:


> Nutzt Du Java8 oder ist hier noch das als _*depricated *_gekennzeichnete _date_ der älteren Version im Einsatz?


Date ist nicht Deprecated.
___

Am einfachsten wäre, den Feldern passende Datentypen zu geben (nicht String für ein Datum!), und dem GSON-Parser den passenden DateFormatter zuzuweisen, dann musst du's nur noch einmalig für die Ausgabe parsen.


----------



## VfL_Freak (15. Sep 2016)

mrBrown hat gesagt.:


> Date ist nicht Deprecated.


Sehe ich etwas anders !!
http://docs.oracle.com/javase/7/docs/api/

Ok, nur weite Teile davon 

Gruß Klaus


----------



## mrBrown (15. Sep 2016)

VfL_Freak hat gesagt.:


> http://docs.oracle.com/javase/7/docs/api/


Wenn du mir jetzt noch zeigst wo es da als Deprecated steht  (Hint: Die Doku zu Date und auch der Source enthalten es beide nicht.)


----------



## VfL_Freak (15. Sep 2016)

Aber gerne doch 

Geh' links oben auf *java.util*, dann unten auf *Date *....
Da sind dann alle (bis einen) Konstruktoren depricated, und das Gros der Methoden !

Gruß Klaus


----------



## InfectedBytes (15. Sep 2016)

Date als ganzes ist (noch?) nicht deprecated, aber dafür jede zweite Methode davon, was meiner Meinung nach ein gutes Indiz gegen die Nutzung davon ist.


----------



## VfL_Freak (15. Sep 2016)

InfectedBytes hat gesagt.:


> Date als ganzes ist (noch?) nicht deprecated, aber dafür jede zweite Methode davon, was meiner Meinung nach ein gutes Indiz gegen die Nutzung davon ist.


Richtig (habe es oben verbessert) 

Hat natürlich nix mit dem *Date* von Java8 zu tun !!


----------



## mrBrown (15. Sep 2016)

InfectedBytes hat gesagt.:


> Date als ganzes ist (noch?) nicht deprecated, aber dafür jede zweite Methode davon, was meiner Meinung nach ein gutes Indiz gegen die Nutzung davon ist.


Die sinds auch schon seit einigen JDK-Versionen (min 1.5, und seit 1.1 gibts zumindest Ersatz dafür), deshalb würd ich erstmal nicht davon ausgehen, dass es so schnell kommt, auch wenn ich sehr dafür wäre, wenn es eher heute als morgen passiert 


Sinnvoll wäre es hier auf jeden Fall, da LocalDate zu benutzen (wenn das nicht also komplex ist, GSON das beizubringen...)


----------



## VfL_Freak (15. Sep 2016)

mrBrown hat gesagt.:


> Die sinds auch schon seit einigen JDK-Versionen (min 1.5, und seit 1.1 gibts zumindest Ersatz dafür), deshalb würd ich erstmal nicht davon ausgehen, dass es so schnell kommt, auch wenn ich sehr dafür wäre, wenn es eher heute als morgen passiert


richtig, des wegen wollte ich anfangs auch "Version*en*" schreiben - das "*en*" am Ende ist wohl bei der Wärme weggeschmolzen 

Wie nutzen hier seit längerem nur noch das oben erwähnte JodaTime, was IMHO wesentlich eleganter ist!

Allerdings es seit Java8 ja eine ganz neue Klasse, die ich aber auch noch nicht ausprobiert habe ...
http://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html
https://kevcodez.de/index.php/2015/07/java-8-date-und-time-api-neue-datumsfunktionen/

Gruß Klaus


----------



## mrBrown (15. Sep 2016)

VfL_Freak hat gesagt.:


> Wie nutzen hier seit längerem nur noch das oben erwähnte JodaTime, was IMHO wesentlich eleganter ist!
> 
> Allerdings es seit Java8 ja eine ganz neue Klasse, die ich aber auch noch nicht ausprobiert habe ...
> http://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html
> https://kevcodez.de/index.php/2015/07/java-8-date-und-time-api-neue-datumsfunktionen/


Die neue Date-API ist ja auch nach JodaTime-Vorbild entstanden, dürfte also ähnlich gut zu benutzen sein, und hat zusätzlich den Vorteil, das es zur Standard-API gehört


----------



## chelayos (16. Sep 2016)

VfL_Freak hat gesagt.:


> Wieso kompliziert ??


Weil ich 5 Zeilen brauche, nur um das Datum ins richtige Format zu bringen xD


VfL_Freak hat gesagt.:


> Was genau ist bei *r.date* das _date_??


Das Feld date im JSON ist ein String. Beispiel:
"2016-03-15T02:25:15"


VfL_Freak hat gesagt.:


> Nutzt Du Java8


Ich habe ehrlich gesagt keine Ahnung. Ich verwende das neueste AndroidStudio mit der neuesten SDK (Android 7). JAVA/Android ist neu für mich. Komme eigentlich aus der C# Ecke.


mrBrown hat gesagt.:


> hat zusätzlich den Vorteil, das es zur Standard-API gehört


Genau, deswegen könnte ich mir dies eher vorstellen zu benutzen. Ich möchte eigentlich keine/wenige Bibliotheken einsetzen, damit meine Androidapp nicht so gross wird.

Noch ne allgemeine Frage. Ich habe vor z.B. oben links ein Menü zu integrieren mit Kategorien (von Wordpress). Ich habe sehe da folgende Möglichkeiten:

1) Hardkodieren / feste URLs setzen. Spart Zeit, da wenig Programmieraufwand. Ressourcenschonend. App startet schneller, da 1x Request weniger gesendet / geparst werden musst. Aber ich muss die App früher oder später anpassen, wenn es neue Kategorien gibt.

2.) Kategorien von Wordpress abgreifen. Somit müsste ich meine App nie wieder ändern.
Aber dann muss bei jedem Appstart noch die Kategorien via Request geholt werden.

3.) Die Premiumvariante währe wohl 2) mit Caching. z.B. 1x täglich bei Wordpress nachfragen, obs neue Kategorien gibt.


----------



## mrBrown (16. Sep 2016)

chelayos hat gesagt.:


> Weil ich 5 Zeilen brauche, nur um das Datum ins richtige Format zu bringen xD


Naja, eigentlich sinds nur 3 Zeilen, und mit passend konfiguriertem GSON sinds drei (bzw eine), kompliziert ist das noch nicht, wenig Zeilen sind selten besser als viele 



chelayos hat gesagt.:


> Das Feld date im JSON ist ein String. Beispiel:
> "2016-03-15T02:25:15"


Lass wie gesagt GSON daraus ein Date machen, dann sparst du dir das.



chelayos hat gesagt.:


> Ich habe ehrlich gesagt keine Ahnung. Ich verwende das neueste AndroidStudio mit der neuesten SDK (Android 7). JAVA/Android ist neu für mich. Komme eigentlich aus der C# Ecke.





chelayos hat gesagt.:


> Genau, deswegen könnte ich mir dies eher vorstellen zu benutzen. Ich möchte eigentlich keine/wenige Bibliotheken einsetzen, damit meine Androidapp nicht so gross wird.



Dann fällt die Java 8-API weg, du wirst also entweder mit dem echt unschönen Date arbeiten müssen, oder externe libs einbinden müssen. JodaTime sollte nicht allzu groß sein.




chelayos hat gesagt.:


> Noch ne allgemeine Frage. Ich habe vor z.B. oben links ein Menü zu integrieren mit Kategorien (von Wordpress). Ich habe she da folgende Möglichkeiten:
> 
> 1) Hardkodieren / feste URLs setzen. Spart Zeit, da wenig Programmieraufwand. Ressourcenschonend. App startet schneller, da 1x Request weniger gesendet / geparst werden musst. Aber ich muss die App früher oder später anpassen, wenn es neue Kategorien gibt.
> 
> ...



Das ganze hinter einem Interface verpacken mit passende Factory z.B., dann kann man es erstmal hardcoded machen, und nachher immernoch an 2) oder 3) anpassen


----------



## Thallius (16. Sep 2016)

Ist das nicht ziemliches POITROAE was Du da machst? Check doch erst einmal wie lange die Abfrage der Kategorien dauert. Weiterhin wäre die Premiumvariante eigentlich

Neustart der App:

Menu ist leer. Du startest einen Background-Task. Dieser holt die Kategorien und setzt sie ins Menu und speichert sie in einer propeties datei. 

alle weiteren Starts:

Laden der Menus aus der property Datei und anzeigen. Gleichzeitig im Background Task die Kategorien holen und wenn fertig in property datei speichern und menu aktualiseren.

Das ist auch nicht wirklich schwer zu implementieren.

Gruß

Claus


----------



## InfectedBytes (16. Sep 2016)

Joda Time kann man auch wunderbar mit Android benutzen. Man muss allerdings einmalig beim Programmstart JodaTimeAndroid.init() aufrufen. Der Rest bleibt gleich


----------

