简体   繁体   English

将JSON绑定到嵌套的Grails域对象

[英]Binding JSON to nested Grails Domain Objects

I'm developing a RESTful interface which is used to provide JSON data for a JavaScript application. 我正在开发一个RESTful接口,用于为JavaScript应用程序提供JSON数据。

On the server side I use Grails 1.3.7 and use GORM Domain Objects for persistence. 在服务器端,我使用Grails 1.3.7并使用GORM域对象进行持久化。 I implemented a custom JSON Marshaller to support marshalling the nested domain objects 我实现了一个自定义JSON Marshaller来支持编组嵌套域对象

Here are sample domain objects: 以下是示例域对象:

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

and

class SampleDomainNested {
    String someField
}

The SampleDomain resource is published under the URL /rs/sample/ so /rs/sample/1 points to the SampleDomain object with ID 1 SampleDomain资源在URL / rs / sample / so / rs / sample / 1下发布,指向ID为1的SampleDomain对象

When I render the resource using my custom json marshaller (GET on /rs/sample/1), I get the following data: 当我使用自定义json marshaller(/ rs / sample / 1上的GET)渲染资源时,我得到以下数据:

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

which is exactly what I want. 这正是我想要的。

Now comes the problem: I try to send the same data to the resource /rs/sample/1 via PUT. 现在出现了问题:我尝试通过PUT将相同的数据发送到资源/ rs / sample / 1。

To bind the json data to the Domain Object, the controller handling the request calls def domain = SampleDomain.get(id) and domain.properties = data where data is the unmarshalled object. 要将json数据绑定到域对象,处理请求的控制器调用def domain = SampleDomain.get(id)domain.properties = data ,其中data是unmarshalled对象。

The binding for the "someString" field is working just fine, but the nested object is not populated using the nested data so I get an error that the property "nest2" is null, which is not allowed. “someString”字段的绑定工作得很好,但是嵌套对象没有使用嵌套数据填充,因此我得到一个错误,即属性“nest2”为null,这是不允许的。

I already tried implementing a custom PropertyEditorSupport as well as a StructuredPropertyEditor and register the editor for the class. 我已经尝试实现自定义PropertyEditorSupport以及StructuredPropertyEditor并注册该类的编辑器。

Strangely, the editor only gets called when I supply non-nested values. 奇怪的是,当我提供非嵌套值时,编辑器才会被调用。 So when I send the following to the server via PUT (which doesn't make any sense ;) ) 所以当我通过PUT将以下内容发送到服务器时(这没有任何意义;))

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

at least the property editor gets called. 至少会调用属性编辑器。

I looked at the code of the GrailsDataBinder . 我查看了GrailsDataBinder的代码。 I found out that setting properties of an association seems to work by specifying the path of the association instead of providing a map, so the following works as well: 我发现通过指定关联的路径而不是提供地图,设置关联的属性似乎有效,因此以下工作原理:

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

but this doesn't help me since I don't want to implement a custom JavaScript to JSON object serializer. 但这对我没有帮助,因为我不想为JSON对象序列化器实现自定义JavaScript。

Is it possible to use Grails data binding using nested maps? 是否可以使用嵌套映射使用Grails数据绑定? Or do I really heave to implement that by hand for each domain class? 或者我是否真的要为每个域类手动实现它?

Thanks a lot, 非常感谢,

Martin 马丁

Since this question got upvoted several times I would like to share what I did in the end: 由于这个问题多次被提出,我想分享我最后做的事情:

Since I had some more requirements to be implemented like security etc. I implemented a service layer which hides the domain objects from the controllers. 由于我有更多要求像安全性等那样实现,我实现了一个服务层,它隐藏了控制器中的域对象。 I introduced a "dynamic DTO layer" which translates Domain Objects to Groovy Maps which can be serialized easily using the standard serializers and which implements the updates manually. 我介绍了一个“动态DTO层”,它将域对象转换为Groovy映射,可以使用标准序列化器轻松序列化并手动实现更新。 All the semi-automatic/meta-programming/command pattern/... based solutions I tried to implement failed at some point, mostly resulting in strange GORM errors or a lot of configuration code (and a lot of frustration). 我尝试实现的所有基于半自动/元编程/命令模式/ ...的解决方案在某些时候都失败了,主要是导致奇怪的GORM错误或许多配置代码(以及很多挫折)。 The update and serialization methods for the DTOs are fairly straightforward and could be implemented very quickly. DTO的更新和序列化方法相当简单,可以非常快速地实现。 It does not introduce a lot of duplicate code as well since you have to specify how your domain objects are serialized anyway if you don't want to publish your internal domain object structure. 它不会引入大量重复代码,因为如果您不想发布内部域对象结构,则必须指定域对象的序列化方式。 Maybe it's not the most elegant solution but it was the only solution which really worked for me. 也许这不是最优雅的解决方案,但它是唯一真正适合我的解决方案。 It also allows me to implement batch updates since the update logic is not connected to the http requests any more. 它还允许我实现批量更新,因为更新逻辑不再连接到http请求。

However I must say that I don't think that grails is the appropriate tech stack best suited for this kind of application, since it makes your application very heavy-weight and inflexbile. 但是我必须说,我不认为grails是最适合这种应用的合适的技术堆栈,因为它使你的应用程序非常重量和inflexbile。 My experience is that once you start doing things which are not supported by the framework by default, it starts getting messy. 我的经验是,一旦你开始做一些默认情况下框架不支持的事情,它就开始变得凌乱。 Furthermore, I don't like the fact that the "repository" layer in grails essentially only exists as a part of the domain objects which introduced a lot of problems and resulted in several "proxy services" emulating a repository layer. 此外,我不喜欢grails中的“repository”层实际上只作为域对象的一部分存在,这引入了许多问题并导致几个“代理服务”模拟存储库层。 If you start building an application using a json rest interface, I would suggest to either go for a very light-weight technology like node.js or, if you want to/have to stick to a java based stack, use standard spring framework + spring mvc + spring data with a nice and clean dto layer (this is what I've migrated to and it works like a charm). 如果您使用json rest接口开始构建应用程序,我建议您选择像node.js这样的轻量级技术,或者,如果您想/必须坚持使用基于Java的堆栈,请使用标准spring框架+ spring mvc + spring数据有一个漂亮干净的dto层(这是我迁移到的,它就像一个魅力)。 You don't have to write a lot of boilerplate code and you are completely in control of what's actually happening. 您不必编写大量的样板代码,而是完全控制实际发生的事情。 Furthermore you get strong typing which increases developer productivity as well as maintainability and which legitimates the additional LOCs. 此外,您可以获得强大的打字,从而提高开发人员的工作效率和可维护性,并使其他LOC合法化。 And of course strong typing means strong tooling! 当然,强大的打字意味着强大的工具!

I started writing a blog entry describing the architecture I came up with (with a sample project of course), however I don't have a lot of time right now to finish it. 我开始写一篇描述我想出的架构的博客文章(当然是一个示例项目),但是我现在没有很多时间来完成它。 When it's done I'm going to link to it here for reference. 当它完成后,我将在这里链接到它以供参考。

Hope this can serve as inspiration for people experiencing similar problems. 希望这可以为遇到类似问题的人们提供灵感。

Cheers! 干杯!

It requires you to provide teh class name: 它要求你提供类名:

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

I know, it requires different input that the output it produces. 我知道,它产生的输出需要不同的输入。

As I mentioned in the comment earlier, you might be better off using the gson library. 正如我之前在评论中提到的,使用gson库可能会更好。

Not sure why you wrote your own json marshaller, with xstream around. 不知道你为什么用xstream编写自己的json marshaller。

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

We have been very happy with xstream for our back end (grails based) services and this way you can render marshall in xml or json, or override the default marshalling for a specific object if you like. 我们对xstream的后端(基于grails)服务非常满意,这样你可以在xml或json中渲染marshall,或者如果你愿意,可以覆盖特定对象的默认编组。

Jettison seems to produce a more compact less human readable JSON and you can run into some library collision stuff, but the default internal json stream renderer is decent. Jettison似乎产生了一个更紧凑,更少人类可读的JSON,你可以遇到一些库碰撞的东西,但默认的内部json流渲染器是体面的。

If you are going to publish the service to the public, you will want to take the time to return appropriate HTTP protocol responses for errors etc... ($.02) 如果要将服务发布给公众,您将需要花时间返回错误等适当的HTTP协议响应...($ .02)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM