Привязка данных из JSON к классу домена неверна для нулевого свойства, если в объекте JSON отсутствует атрибут «класс» - PullRequest
0 голосов
/ 21 декабря 2011

Когда я пытаюсь привязать полезную нагрузку 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".Это .dump() объекта sessionInstance после sessionInstance.properties = params:

<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, но без него все выходит из строя.Я думаю, что привязка данных сможет справиться с этим, поскольку она отлично отображает остальные свойства, но, кажется, просто ужасно терпит неудачу для справки.

Наконец, причина, по которой я столкнулся с этим, заключается в том, чтопотому что я пытаюсь построить клиент Griffon на основе REST API, и кажется, что HTTPBuilder удаляет "class": "server.Session", когда он преобразует исходный ответ JSON в net.sf.json.JSONObject, так как ответ по проводам имеетсвойство в нем, но дамп net.sf.json.JSONObject этого не делает, и при этом полезная нагрузка последующих запросов JSON.

  1. Является ли это ошибкой в ​​том, как Grails связывается с данными, когда нет «класса»свойство объекта JSON?
  2. Это ошибка в том, как HTTPBuilder анализирует ответ JSON?
  3. Это ошибка в том, как HTTPBuilder выводит JSON из net.sf.json.JSONObject?
  4. Я просто что-то делаю не так?

Обновление

Я нашел несколько дополнительных битов информации:

  1. Удаление уникального ограничения не меняет поведение
  2. Если поле uploader имеет значение ненулевое , а внутренний объект JSON пытается установить его на nullсвойство не изменено (и не возникает ошибка).Но если свойство "class": "server.Session" находится во внутренней полезной нагрузке JSON, оно изменяется на null, как и ожидалось.

1 Ответ

0 голосов
/ 21 декабря 2011

IIRC JSONObject Json-lib автоматически удалит специальные свойства, такие как "class" и "metaClass" (в случае POGO). Вы можете настроить это поведение с помощью экземпляра JsonConfig, однако я не знаю, позволяет ли HTTPBuilder внедрить такой экземпляр в соответствующее время.

Альтернативой (типа хакерства) будет отправка значения свойства класса под другим именем (скажем, clazz), которое Json-lib не удалит. Вам нужно зарегистрировать фильтр на стороне Grails, чтобы отобразить «clazz» в «class».

...