【问题标题】:Data binding from JSON on to domain class incorrect for null property when "class" attribute is missing from JSON object当 JSON 对象中缺少“类”属性时,从 JSON 到域类的数据绑定对于 null 属性不正确
【发布时间】:2011-12-21 16:51:45
【问题描述】:

当我尝试将 JSON 有效负载绑定到对另一个域对象具有可为空引用的现有域对象时,如果入站 JSON 指示引用为空,则绑定失败。这会导致持久性尝试失败并出现以下错误:

| Error 2011-12-21 10:21:24,202 ["http-bio-8080"-exec-3] ERROR hibernate.AssertionFailure  - an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
Message: null id in server.Uploader entry (don't flush the Session after an exception occurs)
    Line | Method
->>  105 | update    in server.SessionController
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|   1110 | runWorker in java.util.concurrent.ThreadPoolExecutor
|    603 | run . . . in java.util.concurrent.ThreadPoolExecutor$Worker
^    722 | run       in java.lang.Thread
| Error 2011-12-21 10:21:24,206 ["http-bio-8080"-exec-3] ERROR errors.GrailsExceptionResolver  - AssertionFailure occurred when processing request: [POST] /Server/session/2
null id in server.Uploader entry (don't flush the Session after an exception occurs). Stacktrace follows:
Message: null id in server.Uploader entry (don't flush the Session after an exception occurs)
    Line | Method
->>  105 | update    in server.SessionController
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|   1110 | runWorker in java.util.concurrent.ThreadPoolExecutor
|    603 | run . . . in java.util.concurrent.ThreadPoolExecutor$Worker
^    722 | run       in java.lang.Thread

这是有问题的域对象:

class Session {
    DateTime dateCreated
    DateTime lastUpdated
    String ip
    String state

    static hasOne = [uploader:Uploader]
    static constraints = {
        ip blank: false
        state blank: false
        uploader nullable: true, unique: true
    }
}

class Uploader {
    DateTime dateCreated
    DateTime lastUpdated
    Session session
    String firstName
    String lastName
    String organization
    String phoneNumber
    String emailAddress

    static constraints = {
        firstName blank: false
        lastName blank: false
        organization blank: false
    }
}

这是引发错误的控制器代码:

def update() {
    def sessionInstance = Session.get(params.id)
    if (!sessionInstance) {
        flash.message = message(code: 'default.not.found.message', args: [message(code: 'session.label', default: 'Session'), params.id])
        redirect(action: "list")
        return
    }

    if (params.version) {
        def version = params.version.toLong()
        if (sessionInstance.version > version) {
            sessionInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
                    [message(code: 'session.label', default: 'Session')] as Object[],
                    "Another user has updated this Session while you were editing")
            render(view: "edit", model: [sessionInstance: sessionInstance])
            return
        }
    }

    sessionInstance.properties = params

    if (!sessionInstance.save(flush: true)) {      // <---- Error thrown here
        render(view: "edit", model: [sessionInstance: sessionInstance])
        return
    }

    flash.message = message(code: 'default.updated.message', args: [message(code: 'session.label', default: 'Session'), sessionInstance.id])
    redirect(action: "show", id: sessionInstance.id)
}

这是导致问题的 JSON 负载:

{
"id":2,
"dateCreated":"2011-12-21T09:33:33-06:00",
"ip":"127.0.0.1",
"lastUpdated":"2011-12-21T09:33:33-06:00",
"state":"closed",
"uploader":null
}

但是这个 JSON 有效负载工作得很好:

{
"class": "server.Session",
"id":2,
"dateCreated":"2011-12-21T09:33:33-06:00",
"ip":"127.0.0.1",
"lastUpdated":"2011-12-21T09:33:33-06:00",
"state":"closed",
"uploader":null
}

发生的情况是会话的uploader 属性被设置为"server.Uploader : null"。这是sessionInstance对象在sessionInstance.properties = params之后的.dump()

<server.Session@713c6968 dateCreated=2011-12-21T09:33:33.000-06:00 lastUpdated=2011-12-21T09:33:33.000-06:00 ip=127.0.0.1 state=closed errors=grails.validation.ValidationErrors: 0 errors id=2 version=1 uploader=server.Uploader : null>

"class": "server.Session" 属性位于 JSON 有效负载中时,一切正常,但没有它,一切都会崩溃。我认为数据绑定能够处理这个问题,因为它可以很好地映射其余的属性,但似乎只是因为参考而严重失败。

最后,我遇到此问题的原因是因为我正在尝试针对 REST API 构建 Griffon 客户端,并且似乎 HTTPBuilder 在将原始 JSON 响应转换为 @987654334 时剥离了 "class": "server.Session" @,因为在线响应中包含属性,但 net.sf.json.JSONObject 的转储没有,后续的 JSON 请求负载也没有。

  1. 当 JSON 对象没有“类”属性时,这是 Grails 数据绑定方式中的错误吗?
  2. 这是 HTTPBuilder 如何解析 JSON 响应的错误吗?
  3. HTTPBuilder 如何从 net.sf.json.JSONObject 输出 JSON 是否存在错误?
  4. 我只是做错了什么吗?

更新

我发现了一些额外的信息:

  1. 删除唯一约束不会改变行为
  2. 如果uploader 字段为非空,并且入站JSON 对象尝试将其设置为null,则该属性不会更改(并且不会发生错误)。但是,如果 "class": "server.Session" 属性在入站 JSON 负载中,它会按预期更改为 null

【问题讨论】:

    标签: json data-binding grails httpbuilder


    【解决方案1】:

    IIRC Json-lib 的 JSONObject 将自动去除特殊属性,如“class”和“metaClass”(在 POGO 的情况下)。您可以通过 JsonConfig 实例配置此行为,但是我不知道 HTTPBuilder 是否允许您在适当的时间注入此类实例。

    另一种选择(有点骇人听闻)是用 Json-lib 不会剥离的不同名称(比如 clazz)发送类属性值。您必须在 Grails 端注册一个过滤器才能将“clazz”映射到“class”。

    【讨论】:

    • 是的,这可以作为一种变通方法,但 Grails 的数据绑定不应该足够智能以将属性设置为 null,而不是 "server.Uploader : null"?它清楚地知道它正在使用的 what 属性,因为它找出了 server.Uploader 部分,这是该属性的类型,并且它知道值(null),但它不是完成这项工作。
    • 删除唯一约束的结果,以及一些附加信息,添加到我的问题的末尾。
    猜你喜欢
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 2022-01-15
    • 2021-08-25
    • 2016-05-14
    • 1970-01-01
    • 2018-02-10
    • 2017-02-08
    相关资源
    最近更新 更多