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

发布于 2024-12-06 15:43:39 字数 1551 浏览 1 评论 0原文

我正在开发一个 RESTful 接口,用于为 JavaScript 应用程序提供 JSON 数据。

在服务器端,我使用 Grails 1.3.7 并使用 GORM 域对象来实现持久性。我实现了一个自定义 JSON 编组器来支持编组嵌套域对象

以下是示例域对象:

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

并且

class SampleDomainNested {
    String someField
}

SampleDomain 资源在 URL /rs/sample/ 下发布,因此 /rs/sample/1 指向 ID 为 1 的 SampleDomain 对象

当我使用我的自定义 json 编组器(在 /rs/sample/1 上获取)渲染资源,我得到以下数据:

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

这正是我想要的。

现在问题来了:我尝试通过 PUT 将相同的数据发送到资源 /rs/sample/1 。

要将 json 数据绑定到域对象,处理请求的控制器调用 def domain = SampleDomain.get(id)domain.properties = data,其中 data 是未编组的目的。

“someString”字段的绑定工作正常,但嵌套对象未使用嵌套数据填充,因此我收到属性“nest2”为空的错误,这是不允许的。

我已经尝试实现自定义 PropertyEditorSupport 以及 StructuredPropertyEditor 并注册该类的编辑器。

奇怪的是,只有当我提供非嵌套值时,编辑器才会被调用。因此,当我通过 PUT 将以下内容发送到服务器时(这没有任何意义;))

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

至少会调用属性编辑器。

我查看了 GrailsDataBinder 的代码。我发现设置关联的属性似乎可以通过指定关联的路径而不是提供映射来工作,因此以下内容也可以工作:

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

但这对我没有帮助,因为我不想实现自定义 JavaScript到 JSON 对象序列化器。

是否可以通过嵌套映射来使用 Grails 数据绑定?或者我真的要为每个域类手动实现它吗?

非常感谢,

马丁

I'm developing a RESTful interface which is used to provide JSON data for a JavaScript application.

On the server side I use Grails 1.3.7 and use GORM Domain Objects for persistence. I implemented a custom JSON Marshaller to support marshalling the nested domain objects

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

When I render the resource using my custom json marshaller (GET on /rs/sample/1), I get the following data:

{
    "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.

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.

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.

I already tried implementing a custom PropertyEditorSupport as well as a StructuredPropertyEditor and register the editor for the class.

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 ;) )

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

at least the property editor gets called.

I looked at the code of the 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.

Is it possible to use Grails data binding using nested maps? Or do I really heave to implement that by hand for each domain class?

Thanks a lot,

Martin

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

记忆里有你的影子 2024-12-13 15:43:40

它要求您提供类名:

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

我知道,它需要与其产生的输出不同的输入。

正如我之前在评论中提到的,使用 gson 库可能会更好。

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.

寄人书 2024-12-13 15:43:40

不知道为什么你用 xstream 编写了自己的 json 编组器。

请参阅http://x-stream.github.io/json-tutorial.html

我们对 xstream 为我们的后端(基于 grails)服务感到非常满意,这样您就可以在 xml 或 json 中呈现 marshall,或者如果您愿意,可以覆盖特定对象的默认编组。

Jetison 似乎产生了一个更紧凑、更不可读的 JSON,你可能会遇到一些库冲突的问题,但默认的内部 json 流渲染器是不错的。

如果您打算向公众发布服务,您将需要花时间针对错误等返回适当的 HTTP 协议响应... ($.02)

Not sure why you wrote your own json marshaller, with xstream around.

See 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.

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.

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)

庆幸我还是我 2024-12-13 15:43:39

由于这个问题被多次投票,我想分享一下我最终所做的事情:

因为我有一些更多的要求需要实现,比如安全性等。我实现了一个服务层,它对控制器隐藏了域对象。我引入了一个“动态 DTO 层”,它将域对象转换为 Groovy 映射,可以使用标准序列化器轻松序列化,并手动实现更新。我尝试实现的所有基于半自动/元编程/命令模式/...的解决方案在某些时候都失败了,主要导致奇怪的 GORM 错误或大量配置代码(以及很多挫败感)。 DTO 的更新和序列化方法相当简单,并且可以非常快速地实现。它也不会引入大量重复代码,因为如果您不想发布内部域对象结构,则必须指定域对象的序列化方式。也许这不是最优雅的解决方案,但它是唯一真正对我有用的解决方案。它还允许我实现批量更新,因为更新逻辑不再连接到 http 请求。

然而我必须说,我不认为 grails 是最适合此类应用程序的技术堆栈,因为它使您的应用程序变得非常重量级且不灵活。我的经验是,一旦你开始做框架默认不支持的事情,它就会开始变得混乱。此外,我不喜欢 grails 中的“存储库”层本质上仅作为域对象的一部分存在,这引入了很多问题并导致了几个模拟存储库层的“代理服务”。如果您开始使用 json Rest 接口构建应用程序,我建议要么采用非常轻量级的技术,例如 node.js,或者,如果您想/必须坚持基于 java 的堆栈,请使用标准 spring 框架 + spring mvc + spring data 具有漂亮且干净的 dto 层(这是我迁移到的,它的工作方式就像一个魅力)。您不必编写大量样板代码,并且可以完全控制实际发生的情况。此外,您还可以获得强类型,从而提高开发人员的工作效率和可维护性,并使额外的 LOC 合法化。当然,强大的类型意味着强大的工具!

我开始写一篇博客文章来描述我提出的架构(当然还有一个示例项目),但是我现在没有太多时间来完成它。完成后,我将在此处链接到它以供参考。

希望这可以为遇到类似问题的人带来启发。

干杯!

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. 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). The update and serialization methods for the DTOs are fairly straightforward and could be implemented very quickly. 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.

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. 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. 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). 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. 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!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文