JSON flatten und wieder in DTO konvertieren

Avalon

Bekanntes Mitglied
Hallo, ich habe eine klassische Spring Boot MVC Anwendung und versuche dort gerade ein JSON Objekt, das aus 3 verschachtelten DTOs besteht, zu flatten und wieder in ein DTO zu konvertieren. (Jackson, JsonFlattener, mapstruct). Aber wie zum Geier, kann ich aus dem Flat JSON wieder ein DTO bauen. Es hat zwar schon funktioniert. Für den User sind die Values auch drin. Aber für die eingebetten Collections, fehlen die Werte (null). Ist eigentlich auch klar, weil die unbekannt und nicht gemappt sind. Aber wie kann man das mit mapstruct mappen (in die Richtig geht das doch, oder?). Alles was ich ich jetzt bei Google gefunden habe, hat mir leider nicht weiter geholfen. jsonschema2pojo habe ich bemüht. objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); brachte auch nicht das gewünschte Ergebnis. Ich weiß nicht mehr weiter.
[CODE lang="java" title="UserDto"]@Getter @Setter
public class UserDto {

private Long id;

@NotNull
@NotEmpty
private String firstname;

@NotNull
@NotEmpty
private String lastname;

@NotNull
@NotEmpty
private String username;

@NotNull
@NotEmpty
private String email;

@NotNull
@NotEmpty
private String password;
private String passwordconfirm;
private boolean enabled;
private boolean tokenexpired;
private Collection<RoleDto> roles;
}[/CODE]
[CODE lang="java" title="RoleDto"]@Getter @Setter
public class RoleDto {

private String name;
private Collection<PrivilegeDto> privileges;
}[/CODE]
[CODE lang="java" title="PrivilegeDto"]@Getter @Setter
public class PrivilegeDto {
private Long id;
private String name;

}[/CODE]

Bis zum Flat JSON bin ich gekommen. Das liegt als String vor und sieht so aus...
[CODE lang="java" title="UserDto JSON Flat als String"]{
"id": 1,
"firstname": "Test",
"lastname": "Test",
"username": "Avalon",
"email": "test@test.com",
"password": "$2a$10$.zywwDazLvC9srdDWuhp3eAvlVpolETRpUY4hQ4soq.j0Jsygu.gy",
"passwordconfirm": null,
"enabled": true,
"tokenexpired": false,
"roles[0].name": "ROLE_ADMIN",
"roles[0].privileges[0].id": 1,
"roles[0].privileges[0].name": "READ_PRIVILEGE",
"roles[0].privileges[1].id": 2,
"roles[0].privileges[1].name": "WRITE_PRIVILEGE"
}[/CODE]
 

Oneixee5

Top Contributor
XML:
<dependency>
    <groupId>com.github.wnameless</groupId>
    <artifactId>json-flattener</artifactId>
    <version>????</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>????</version>
</dependency>
Java:
String nestedJson = JsonUnflattener.unflatten(flattenedJson);
ObjectMapper objectMapper = new ObjectMapper();
UserDto dto = objectMapper.readValue(nestedJson, UserDto.class);
 
Zuletzt bearbeitet:

Avalon

Bekanntes Mitglied
Da hab ich mich wohl falsch ausgedrückt. Wenn ich den geflatteten JSON String in String nestedJson = JsonUnflattener.unflatten(flattenedJson); reinpacke, kommt ein ungeflattetes Object raus. Das will ich ja nicht. Das sieht dann bei mir so aus.
[CODE lang="json" title="unflattes nested JSON"]{
"id": 1,
"firstname": "Test",
"lastname": "Test",
"username": "Avalon",
"email": "test@test.com",
"password": "$2a$10$.zywwDazLvC9srdDWuhp3eAvlVpolETRpUY4hQ4soq.j0Jsygu.gy",
"passwordconfirm": null,
"enabled": true,
"tokenexpired": false,
"roles": [
{
"name": "ROLE_ADMIN",
"privileges": [
{
"id": 1,
"name": "READ_PRIVILEGE"
},
{
"id": 2,
"name": "WRITE_PRIVILEGE"
}
]
}
]
}[/CODE]
 

Avalon

Bekanntes Mitglied
Das ist eine andere Frage. Ich hab da auch googln müssen (Key:Value, Sieht schöner aus, Belastet den Traffic weniger, die Datenbank usw.). Soweit ich das mitbekommen habe, ist das nur ab bestimmten Größenordnungen wichtig. Ist aber auch egal. Ich muss das abliefern, sonst Kopf kürzer :-D
 

Avalon

Bekanntes Mitglied
Also versuch nochmal anhand einer Zeile zu beschreiben was ich brauche. Wenn ich den oben beschrieben Flat String wieder in ein DTO Object konvertiere und die flache Struktur erhalten bleiben soll, erhalte ich in der Zeile
Code:
"roles[0].name": "ROLE_ADMIN",
einen "null" Wert. Also
Code:
"roles[0].name": "null",
. Ich muss es irgendwie schaffen, mittels mapstruct oder wie auch immer den Wert
Code:
"ROLE_ADMIN"
auf
Code:
"roles[0].name"
zu mappen. Das ist zwar auch falsch ausgedrückt. Aber hoffe jemand weiß was ich meine. Mit der "Fachlichkeit" hab ich es nicht so.
 

mrBrown

Super-Moderator
Mitarbeiter
Was hast du denn genau und was genau brauchst du?

Hast du Java-DTOs, und brauchst "flatted JSON"? Dann DTO mit Jackson zu JSON und den Json-String dann flatten.
Oder hast du "flatted JSON"? Dann unflatten und den String mit Jackson normal deserialisieren.


BTW: das DTO sollte in der Form besser niemals benutzt werden. password, passwordconfirm, enabled, tokenexpired und roles sollten niemals zusammen in einem DTO stehen, damit provoziert man nur Datenleaks und Sicherheitslücken.

Key:Value, Sieht schöner aus, Belastet den Traffic weniger, die Datenbank usw.). Soweit ich das mitbekommen habe, ist das nur ab bestimmten Größenordnungen wichtig.
"geflattet" ist es größer, in deinem Beispiel sind's grob 20% (keine Ahnung, vielleicht gib's auch Fälle in denen es kleiner ist, aber leserlicher wird's damit garantiert nicht), Traffic ist also eher weniger 'n Argument.

Ich muss das abliefern, sonst Kopf kürzer :-D
Ich hoffe, das ist keine Ausbildung oä o_O
 

Avalon

Bekanntes Mitglied
Das hab ich jetzt bei einem GET-Request auf http://localhost:8080/user/1
JSON:
{
    "id": 1,
    "firstname": "Test",
    "lastname": "Test",
    "username": "Avalon",
    "email": "test@test.com",
    "password": "$2a$10$.zywwDazLvC9srdDWuhp3eAvlVpolETRpUY4hQ4soq.j0Jsygu.gy",
    "passwordconfirm": null,
    "enabled": true,
    "tokenexpired": false,
    "roles": [
        {
            "name": "ROLE_ADMIN",
            "privileges": [
                {
                    "id": 1,
                    "name": "READ_PRIVILEGE"
                },
                {
                    "id": 2,
                    "name": "WRITE_PRIVILEGE"
                }
            ]
        }
    ]
}
Und so soll es aussehen (und auch ein Object sein)

JSON:
{
    "id": 1,
    "firstname": "Test",
    "lastname": "Test",
    "username": "Avalon",
    "email": "test@test.com",
    "password": "$2a$10$.zywwDazLvC9srdDWuhp3eAvlVpolETRpUY4hQ4soq.j0Jsygu.gy",
    "passwordconfirm": null,
    "enabled": true,
    "tokenexpired": false,
    "roles[0].name": "ROLE_ADMIN",
    "roles[0].privileges[0].id": 1,
    "roles[0].privileges[0].name": "READ_PRIVILEGE",
    "roles[0].privileges[1].id": 2,
    "roles[0].privileges[1].name": "WRITE_PRIVILEGE"
}

Geht das irgendwie?
 

mrBrown

Super-Moderator
Mitarbeiter
Hier:
Hast du Java-DTOs, und brauchst "flatted JSON"? Dann DTO mit Jackson zu JSON und den Json-String dann flatten.

Vielleicht gibt's irgendeine Lib für Spring, die das automatisch macht, aber keine Ahnung ob sowas existiert. Alternativ könnte man sicherlich nen eigenen MediaType nutzen und 'nen MessageConverter dafür schreiben, dann hat man die Logik dafür gekapselt und vom eigentlichen Code getrennt.
 

Avalon

Bekanntes Mitglied
Ich hab bis jetzt leider nichts gefunden. Es gibt hunderte Tutorials, wie man das mit "normalen" DTOs macht die verschachtelt sind. Das ist sogar richtig einfach. Aber in keinem davon sind Collections, Listen oder Sets in den Klassenvariablen. Ich weiß einfach nicht, wie ich in Mapstruct "roles[0].name" den Wert "ROLE_ADMIN" zuweisen bzw. ihn mappen kann. Der steckt bei Collection<RoleDto> mit drin.
 

mrBrown

Super-Moderator
Mitarbeiter
An welcher Stelle soll dabei überhaupt Mapstruct zum Einsatz kommen? Mit Mapstruct mappt man zwischen Java-Klassen, in deinem Fall willst du aber von JSON auf die Java-Klasse mappen.

Und wie flatted-Json -> unflatted-Json -> DTO möglich ist, steht in #2:
XML:
<dependency>
    <groupId>com.github.wnameless</groupId>
    <artifactId>json-flattener</artifactId>
    <version>????</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>????</version>
</dependency>
Java:
String nestedJson = JsonUnflattener.unflatten(flattenedJson);
ObjectMapper objectMapper = new ObjectMapper();
UserDto dto = objectMapper.readValue(nestedJson, UserDto.class);
 

mrBrown

Super-Moderator
Mitarbeiter
@Avalon: Kannst du noch mal ganz einfach mit 4 Wörtern sagen, was genau du an Daten hast (Java-Entity, Java-DTO, Json, flatted-Json, ...) und was genau du an Daten brauchst (Java-Entity, Java-DTO, Json, flatted-Json, ...)? Einfach nur ein "Habe: X, brauchte: Y"
 

Avalon

Bekanntes Mitglied
Ich schieb hier einfach den wichtigen Teil aus meinem Controller ungefiltert rein. ACHTUNG !!!! BAUSTELLE!!! So hat es angefangen mit dem Fehler

"There was an unexpected error (type=Internal Server Error, status=500).
Unrecognized field "roles[0].name" (class de.pixpartout.my.dto.UserDto), not marked as ignorable (10 known properties: "lastname", "passwordconfirm", "username", "id", "email", "roles", "enabled", "firstname", "tokenexpired", "password"]) at [Source: (String)""

JSON:
@GetMapping(path = "/user/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
    public UserDto getById(@PathVariable Long id)
            throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        //objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        UserDto userDto = userMapper.mapToDto(userService.getByID(id));       
        
        //Json Objekt zu String konvertieren
        String json = objectMapper.writeValueAsString(userDto);
        System.out.println("#######################");
        System.out.println(json);
        System.out.println("#######################");
        
        //FlattenJson aus String erstellen
        String flattenJson = JsonFlattener.flatten(json);
        //Map<String, Object> flattenJson = JsonFlattener.flattenAsMap(json);
        System.out.println("#######################");
        System.out.println(flattenJson);
        System.out.println("#######################");
        
        //FlattenJson wieder in Object konvertieren
        userDto = objectMapper.readValue(flattenJson, UserDto.class);
        
        return userDto;
    }
 

Avalon

Bekanntes Mitglied
Der Controller ist Mist, und Anfangen aus Mist zu lesen, was du eigentlich willst, wollen wir hier nicht :)
Chef? Bist Du das? :) Kommt mir alles irgendwie bekannt vor. :)


OK. Ich hab erstmal alles in den Controller reingepackt. Wenn es funktioniert wollte ich es auslagern. Aber in Worten beschreiben...puuh, ich muss überlegen? Ich fang mal beim Urschleim an. Eigentlich alles Standard (Spring Boot REST). Meine Packages in Eclipse sind Entity, Controller, Service, Mapper, Repository und DTO. Alles läuft über JPA/Hibernate und MariaDB. Bei einem Get Request wird die entsprechende Methode in der ServiceImplementation aufgerufen, die holt die Daten aus der Datenbank mappt das Ganze per Mapstruct auf ein DTO (normalerweise nur die benötigten Properties) und gibt das DTO zurück an den Clienten. Fehlt noch was? Der Controller sah bis jetzt so aus.

Java:
    @GetMapping(path = "/user/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
    public UserDto getById(@PathVariable Long id) {
        return userMapper.mapToDto(userService.getByID(id));
    }
Im zurückgegeben JSON Object UserDto sind weitere DTOs. Collection<RoleDto> und Collection<PrivilegeDto> mit den dazugehörigen Werten. Das sieht dann so aus.
JSON:
{
    "id": 1,
    "firstname": "Test",
    "lastname": "Test",
    "username": "Avalon",
    "email": "test@test.com",
    "password": "$2a$10$.zywwDazLvC9srdDWuhp3eAvlVpolETRpUY4hQ4soq.j0Jsygu.gy",
    "passwordconfirm": null,
    "enabled": true,
    "tokenexpired": false,
    "roles": [
        {
            "name": "ROLE_ADMIN",
            "privileges": [
                {
                    "id": 1,
                    "name": "READ_PRIVILEGE"
                },
                {
                    "id": 2,
                    "name": "WRITE_PRIVILEGE"
                }
            ]
        }
    ]
}
Und dieses JSON Object ist der aktuelle Auslieferungszsutand an den Clienten, soll aber bevor es an den Client ausgeliefert wird, geflattet werden.
Ziel: Der Client soll ein JSON Object (Userdaten samt Rollen und Privilegien) bekommen, nur mit Key:Value Paaren, ohne Arrays.

JSON:
{
    "id": 1,
    "firstname": "Test",
    "lastname": "Test",
    "username": "Avalon",
    "email": "test@test.com",
    "password": "$2a$10$.zywwDazLvC9srdDWuhp3eAvlVpolETRpUY4hQ4soq.j0Jsygu.gy",
    "passwordconfirm": null,
    "enabled": true,
    "tokenexpired": false,
    "roles[0].privileges[0].id": 1,
    "roles[0].privileges[0].name": "READ_PRIVILEGE",
    "roles[0].privileges[1].id": 2,
    "roles[0].privileges[1].name": "WRITE_PRIVILEGE",
    "roles[0].name": "ROLE_ADMIN"
}
 

mrBrown

Super-Moderator
Mitarbeiter
Chef? Bist Du das? :)
Ne, dann hättest du sinnvolle Aufgaben bekommen :)
Und dieses JSON Object ist der aktuelle Auslieferungszsutand an den Clienten, soll bevor es an den Client ausgeliefert wird, geflattet werden.
Okay :)

Der "stumpfeste Weg" wäre, das Json-Objekt selbst in der Controller-Methode zu erzeugen und zu flatten, etwa so:

Java:
UserDto dto = userMapper.mapToDto(userService.getByID(id));
String json = objectMapper.writeValueAsString(dto);
return JsonFlattener.flatten(json);

Der return-Type muss dann String werden.


Theoretisch kann man sicherlich irgendwie das Flatten auslagern, damit der Controller weiterhin schön ist und nur ein UserDto zurück gibt.
Möglich wäre da entweder ein eigener MediaType (kommt auf den Client an, ob das geht) oder ein Wrapper (sowas wie class Flat<T> {...}), und dafür registriert man dann einen eigenen HttpMessageConverter, der das ganze zu flatted Json serialisiert (und nötigenfalls deserialisiert).
 

Avalon

Bekanntes Mitglied
OK Danke. Den stumpfen Weg hatte ich schon. Aber den return-type als String zu übergeben bzw. zu belassen, daran habe ich jetzt nicht gedacht, weil Objekt Vorgabe war. Mal sehen ob man das so lassen kann. Hoffentlich war das nicht Sinn und Zweck der Aufgabe. Darauf zu kommen! :) Trotzdem Danke. Jetzt muss ich das alles nur noch in ein Thymeleaf Template unterbringen, was meine css Datei nicht im Thymeafstandardordner für css Dateien findet. Und fertig. :)
 

mrBrown

Super-Moderator
Mitarbeiter
OK Danke. Den stumpfen Weg hatte ich schon. Aber den return-type als String zu übergeben bzw. zu belassen, daran habe ich jetzt nicht gedacht, weil Objekt Vorgabe war. Mal sehen ob man das so lassen kann. Hoffentlich war das nicht Sinn und Zweck der Aufgabe. Darauf zu kommen! :)
Gibts noch andere Vorgaben, die du uns bisher verschwiegen hast? ;) Je besser man die eigentlichen Anforderungen kennt, desto besser kann man helfen – und gleichzeitig: je mehr jemand, der Hilfe braucht, selbst die Anforderungen versucht zu interpretieren, desto schlechter kann man helfen :)

Wenn zB alle Endpunkte immer flatted Json zurückgeben sollen, könnte man das an einer Stelle einstellen, und die Controller geben nur die DTOs direkt zurück.
Oder vielleicht sollen auch konkrete DTOs so zurückgegeben werden, dann könnte man die DTOs entsprechende markieren.
Oder vielleicht sollen auch nur bestimmte Endpunkte flatted Json zurückgeben, dann könnte man über Wrapper-Typen nachdenken.
Oder vielleicht soll auch der Client das entscheiden, dann wäre MediaTypes uU ein guter Ansatz.


Und um das noch mal zu wiederholen: ein DTO in der Form sollte nicht existieren. Vielleicht war auch der ganze Sinn der Aufgabe, etwas gegen dieses DTO zu unternehmen :)
 

Avalon

Bekanntes Mitglied
Wenn zB alle Endpunkte immer flatted Json zurückgeben sollen, könnte man das an einer Stelle einstellen, und die Controller geben nur die DTOs direkt zurück.
Oder vielleicht sollen auch konkrete DTOs so zurückgegeben werden, dann könnte man die DTOs entsprechende markieren.
Oder vielleicht sollen auch nur bestimmte Endpunkte flatted Json zurückgeben, dann könnte man über Wrapper-Typen nachdenken.
Oder vielleicht soll auch der Client das entscheiden, dann wäre MediaTypes uU ein guter Ansatz.


Und um das noch mal zu wiederholen: ein DTO in der Form sollte nicht existieren. Vielleicht war auch der ganze Sinn der Aufgabe, etwas gegen dieses DTO zu unternehmen :)
An all das hab ich auch schon gedacht... Aaaaaaaaber DAAAAAS
Gibts noch andere Vorgaben, die du uns bisher verschwiegen hast? ;) Je besser man die eigentlichen Anforderungen kennt, desto besser kann man helfen – und gleichzeitig: je mehr jemand, der Hilfe braucht, selbst die Anforderungen versucht zu interpretieren, desto schlechter kann man helfen :)
muss ich erstmal sacken lassen und dafür meinen Babel Fish füttern. :)
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
B Json Objekt sinnvoll plätten? XML & JSON 1
W com.android.volley.ParseError: org.json.JSONException: End of input at character 0 of XML & JSON 6
L JSON auslesen und Labels in GUI verwenden XML & JSON 13
mananana Frage zu JSON XML & JSON 3
wofus JSON filtern nach bestimmten Wert XML & JSON 7
N JSON export String Unicode? XML & JSON 6
L Json reader XML & JSON 15
megusta JSON umschreiben (converter)? XML & JSON 4
P verschachteltes json verändern XML & JSON 3
M Objekt zu jsonArray in .json datei hinzufügen ? XML & JSON 3
Z json inkl. Array/Verschachtelung erstellen XML & JSON 2
J Object in JSON Datei einlesen und als neues Object erzeugen (in ein Object Array) XML & JSON 29
Trèfle Formatierung v. JSON File XML & JSON 7
M json page 2 auslesen XML & JSON 1
K JSON mit GSON nutzen XML & JSON 4
S Java REST Client + Json XML & JSON 8
W Search Value in Json XML & JSON 6
K JSON-Bibliothek XML & JSON 5
M Großes Json Objekt benutzen XML & JSON 5
M Json auslesen XML & JSON 7
M Xml oder Json? XML & JSON 15
J JSON zu Java mit der GSON Google API XML & JSON 0
G Jackson JSON: Dynamische Serialisierung XML & JSON 6
W Json von URL Lesen (mit Gson Library) XML & JSON 3
W Json von URL Lesen XML & JSON 2
L Jackson JSON: Probleme beim einlesen XML & JSON 1
J JSon-Converter gesucht XML & JSON 2
Q Konvertierung von json zum Java Objekt nach vorgegebenem Schema XML & JSON 3
J JSon <-> XML zwecks Validierung XML & JSON 4
M [JSON] Wie Splitten? XML & JSON 14
G Mit JSON Java und C# verbinden XML & JSON 4
I Liste in YAML Datei speichern und wieder auslesen XML & JSON 1
R XML eingelesen und wieder geschrieben... Fehler XML & JSON 7
F XML erzeugen aus JTree und wieder einlesen... XML & JSON 6
D JTree aus XML Datei erstellen & JTree wieder in XML spei XML & JSON 4
S Mal wieder Sax was sonst ;( XML & JSON 5
S Geändertes XML-File wieder abspeichern. XML & JSON 10
P XML - mal wieder. XML & JSON 10
N Objekte via XML speichern und wieder einlesen XML & JSON 12

Ähnliche Java Themen

Neue Themen


Oben