39 votes

Lier JSON à des objets de domaine Grails imbriqués

Je développe une interface RESTful qui est utilisée pour fournir des données JSON à une application JavaScript.

Côté serveur, j'utilise Grails 1.3.7 et j'utilise les objets de domaine GORM pour la persistance. J'ai implémenté un Marshaller JSON personnalisé pour prendre en charge le marshalling des objets de domaine imbriqués.

Voici des exemples d'objets de domaine :

class SampleDomain {
    static mapping = { nest2 cascade: 'all' }
    String someString
    SampleDomainNested nest2
}

y

class SampleDomainNested {
    String someField
}

La ressource SampleDomain est publiée sous l'URL /rs/sample/, donc /rs/sample/1 pointe vers l'objet SampleDomain avec l'ID 1.

Lorsque je rends la ressource en utilisant mon marshaller json personnalisé (GET sur /rs/sample/1), j'obtiens les données suivantes :

{
    "someString" : "somevalue1",
    "nest2" : {
        "someField" : "someothervalue"
    }
}

ce qui est exactement ce que je veux.

Maintenant vient le problème : j'essaie d'envoyer les mêmes données à la ressource /rs/sample/1 via PUT.

Pour lier les données json à l'objet de domaine, le contrôleur traitant la demande appelle def domain = SampleDomain.get(id) y domain.properties = data où les données sont les objets non marqués.

La liaison pour le champ "someString" fonctionne parfaitement, mais l'objet imbriqué n'est pas alimenté par les données imbriquées et je reçois une erreur indiquant que la propriété "nest2" est nulle, ce qui n'est pas autorisé.

J'ai déjà essayé d'implémenter un PropertyEditorSupport ainsi qu'un StructuredPropertyEditor et enregistrer l'éditeur pour la classe.

Bizarrement, l'éditeur n'est appelé que lorsque je fournis des valeurs non imbriquées. Ainsi, lorsque j'envoie ce qui suit au serveur via PUT (ce qui n'a aucun sens ;) )

{
    "someString" : "somevalue1",
    "nest2" : "test"
}

au moins l'éditeur de propriétés est appelé.

J'ai regardé le code de la GrailsDataBinder . J'ai découvert que la définition des propriétés d'une association semble fonctionner en spécifiant le chemin de l'association au lieu de fournir une carte, donc ce qui suit fonctionne également :

{
    "someString" : "somevalue1",
    "nest2.somefield" : "someothervalue"
}

mais cela ne m'aide pas puisque je ne veux pas mettre en œuvre un sérialiseur d'objets JavaScript vers JSON personnalisé.

Est-il possible d'utiliser la liaison de données Grails en utilisant des cartes imbriquées ? Ou dois-je vraiment l'implémenter à la main pour chaque classe de domaine ?

Merci beaucoup,

Martin

7voto

frow Points 529

Puisque cette question a été votée plusieurs fois, je voudrais partager ce que j'ai fait à la fin :

Comme j'avais d'autres exigences à mettre en œuvre, comme la sécurité, etc. J'ai implémenté une couche de service qui cache les objets du domaine aux contrôleurs. J'ai introduit une "couche DTO dynamique" qui traduit les objets de domaine en cartes Groovy pouvant être sérialisées facilement à l'aide des sérialiseurs standard et qui met en œuvre les mises à jour manuellement. Toutes les solutions semi-automatiques/métaprogrammation/command pattern/... que j'ai essayé d'implémenter ont échoué à un moment ou à un autre, entraînant le plus souvent des erreurs GORM étranges ou beaucoup de code de configuration (et beaucoup de frustration). Les méthodes de mise à jour et de sérialisation pour les DTO sont assez simples et pourraient être mises en œuvre très rapidement. Elles n'introduisent pas non plus beaucoup de code en double puisque vous devez de toute façon spécifier comment vos objets de domaine sont sérialisés si vous ne voulez pas publier votre structure interne d'objets de domaine. Ce n'est peut-être pas la solution la plus élégante, mais c'est la seule qui ait vraiment fonctionné pour moi. Elle me permet également de mettre en œuvre des mises à jour par lots, puisque la logique de mise à jour n'est plus liée aux requêtes http.

Cependant, je dois dire que je ne pense pas que Grails soit la pile technologique la mieux adaptée à ce type d'application, car elle rend votre application très lourde et inflexible. D'après mon expérience, dès que vous commencez à faire des choses qui ne sont pas prises en charge par le framework par défaut, cela commence à devenir désordonné. De plus, je n'aime pas le fait que la couche "dépôt" dans Grails n'existe essentiellement que comme une partie des objets de domaine, ce qui a introduit beaucoup de problèmes et a donné lieu à plusieurs "services proxy" émulant une couche dépôt. Si vous commencez à construire une application utilisant une interface de repos json, je vous suggère d'opter pour une technologie très légère comme node.js ou, si vous voulez/devez vous en tenir à une pile basée sur Java, d'utiliser le cadre standard spring + spring mvc + spring data avec une couche dto propre et agréable (c'est ce vers quoi j'ai migré et cela fonctionne comme un charme). Vous n'avez pas à écrire beaucoup de code passe-partout et vous avez un contrôle total sur ce qui se passe réellement. En outre, vous bénéficiez d'un typage fort qui augmente la productivité des développeurs ainsi que la maintenabilité et qui légitime les LOC supplémentaires. Et bien sûr, qui dit typage fort dit outillage fort !

J'ai commencé à écrire un article de blog décrivant l'architecture que j'ai mise au point (avec un exemple de projet bien sûr), mais je n'ai pas beaucoup de temps pour le terminer. Quand il sera terminé, je le mettrai en lien ici pour référence.

J'espère que cela pourra servir d'inspiration aux personnes qui rencontrent des problèmes similaires.

A la vôtre !

1voto

SecretService Points 1469

Il vous demande de fournir le nom de la classe :

{ class:"SampleDomain", someString: "abc", 
nest2: { class: "SampleDomainNested", someField:"def" }
} 

Je sais, ça nécessite une entrée différente de la sortie qu'il produit.

Comme je l'ai mentionné dans le commentaire précédent, vous feriez mieux d'utiliser la bibliothèque gson.

1voto

Todd Ellermann Points 56

Je ne sais pas pourquoi vous avez écrit votre propre marshaller json, avec xstream autour.

Voir http://x-stream.github.io/json-tutorial.html

Nous avons été très satisfaits de xstream pour nos services back-end (basés sur Grails) et de cette façon, vous pouvez rendre le marshall en xml ou json, ou remplacer le marshalling par défaut pour un objet spécifique si vous le souhaitez.

Jettison semble produire un JSON plus compact et moins lisible par l'homme, et vous pouvez rencontrer des problèmes de collision avec la bibliothèque, mais le générateur de flux json interne par défaut est décent.

Si vous allez publier le service au public, vous voudrez prendre le temps de renvoyer les réponses appropriées du protocole HTTP pour les erreurs, etc... ($.02)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X