【问题标题】:Binding JSON to nested Grails Domain Objects将 JSON 绑定到嵌套的 Grails 域对象
【发布时间】:2011-11-23 13:09:56
【问题描述】:

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

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

以下是示例域对象:

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 编组器(GET on /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 数据绑定?还是我真的要为每个域类手动实现它?

非常感谢,

马丁

【问题讨论】:

  • 你也有自定义的 json unmarshaller 吗?
  • 不,我没有自定义 json 解组器。我使用 request.JSON 解析请求。我想要的是一个属性编辑器,它支持从 Map 创建域对象以及按 ID 加载/映射域对象。
  • 你试过这个插件了吗:grails.org/plugin/json-rest-api
  • 感谢您的提示。我试过这个插件,但它只支持“平面”域对象,即所有关联都呈现为 ID。但是我在那个插件中找到了一些灵​​感。我想我要实现一个映射器,它的行为由域对象中的一些静态控制(即资源 url、引用的属性、嵌入的属性......)
  • 你试过 gson 吗?你可能会更好地使用它......但我相信有一种方法可以通过 grails 方式实现。

标签: json data-binding grails grails-orm


【解决方案1】:

由于这个问题被多次投票,我想分享一下我最后做了什么:

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

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

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

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

干杯!

【讨论】:

  • 感谢您的见解,我觉得它很有帮助。我最近开始使用 Grails 构建一个项目,我得出的结论与您所做的相同。我选择 Grails 了解权衡取舍、许多优点和缺点。但是,这种对取消编组复杂对象的限制令我大吃一惊。现在,我又想了想,回到 Spring MVC + Spring Data。我唯一会想念的是普惠制。我发现 GSP,尤其是 sitemesh,非常强大。你知道有什么可以和 GSP 一样好的 Spring MVC 使用的技术吗?
  • 看看 Thymeleaf。它的目标与普惠制非常相似。
【解决方案2】:

需要你提供类名:

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

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

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

【讨论】:

    【解决方案3】:

    不知道你为什么要编写自己的 json marshaller,并使用 xstream。

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

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

    Jettison 似乎生成了一个更紧凑、不太可读的 JSON,并且您可能会遇到一些库冲突的东西,但默认的内部 json 流渲染器是不错的。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-01
      • 2017-08-30
      相关资源
      最近更新 更多