Массив Grails JSON - PullRequest
       26

Массив Grails JSON

11 голосов
/ 20 апреля 2010

Я преобразую список объектов Foo в строку JSON. Мне нужно разобрать строку JSON обратно в список Foos. Однако в следующем примере синтаксический анализ дает мне список JSONObjects вместо Foos. * ** 1003 тысяча два * Пример

List list = [new Foo("first"), new Foo("second")]
def jsonString = (list as JSON).toString()

List parsedList = JSON.parse(jsonString) as List
println parsedList[0].getClass() // org.codehaus.groovy.grails.web.json.JSONObject

Как я могу вместо этого разобрать его в Foos? Заранее спасибо.

Ответы [ 4 ]

12 голосов
/ 20 апреля 2010

Я посмотрел документацию по API для JSON, и, похоже, нет никакого способа проанализировать строку JSON для определенного типа объекта.

Так что вам просто нужно написать код самостоятельно, чтобы преобразовать каждый JSONObject в Foo. Примерно так должно работать:

import grails.converters.JSON
import org.codehaus.groovy.grails.web.json.*

class Foo {
  def name

  Foo(name) {
    this.name = name
  }

  String toString() {
    name
  }
}


List list = [new Foo("first"), new Foo("second")]
def jsonString = (list as JSON).toString()

List parsedList = JSON.parse(jsonString)

// Convert from a list of JSONObject to a list of Foo
def foos = parsedList.collect {JSONObject jsonObject ->
    new Foo(name: jsonObject.get("name"))
}

Более общим решением было бы добавить новый статический метод parse, такой как следующий, в метакласс JSON, который пытается проанализировать строку JSON в Список объектов определенного типа:

import grails.converters.JSON
import org.codehaus.groovy.grails.web.json.*

class Foo {
  def name

  Foo(name) {
    this.name = name
  }

  String toString() {
    name
  }
}

List list = [new Foo("first"), new Foo("second")]
def jsonString = (list as JSON).toString()


List parsedList = JSON.parse(jsonString)

// Define the new method
JSON.metaClass.static.parse = {String json, Class clazz ->

    List jsonObjs = JSON.parse(json)

    jsonObjs.collect {JSONObject jsonObj ->

        // If the user hasn't provided a targetClass read the 'class' proprerty in the JSON to figure out which type to convert to
        def targetClass = clazz ?: jsonObj.get('class') as Class
        def targetInstance = targetClass.newInstance()        

        // Set the properties of targetInstance
        jsonObj.entrySet().each {entry ->

            if (entry.key != "class") {
                targetInstance."$entry.key" = entry.value
            }
        }
        targetInstance
    }

}

// Try the new parse method
List<Foo> foos = JSON.parse(jsonString, Foo)

// Confirm it worked
assert foos.every {Foo foo -> foo.class == Foo && foo.name in ['first', 'second'] }

Вы можете попробовать приведенный выше код в отличной консоли. Несколько предупреждений

  • Я только что провел очень ограниченное тестирование с кодом выше
  • В последнем выпуске Grails есть два класса JSON, я предполагаю, что вы используете тот, который не устарел
4 голосов
/ 03 октября 2011

Я взял этот код и расширил его для работы с вложенными структурами. Он опирается на атрибут «класс», существующий в JSON. Если в Граале есть лучший путь, пожалуйста, дайте мне знать.

     // The default JSON parser just creates generic JSON objects.  If there are nested

    // JSON arrays they are not converted to theirs types but are left as JSON objects
    // This converts nested JSON structures into their types.
    // IT RELIES ON A PROPERTY 'class' that must exist in the JSON tags
    JSON.metaClass.static.parseJSONToTyped = {def jsonObjects ->

        def typedObjects = jsonObjects.collect {JSONObject jsonObject ->
            if(!jsonObject.has("class")){
                throw new Exception("JSON parsing failed due to the 'class' attribute missing: " + jsonObject)
            }

            def targetClass = grailsApplication.classLoader.loadClass(jsonObject.get("class"))
            def targetInstance = targetClass.newInstance()

            // Set the properties of targetInstance
            jsonObject.entrySet().each {entry ->
                // If the entry is an array then recurse
                if(entry.value instanceof org.codehaus.groovy.grails.web.json.JSONArray){
                    def typedSubObjects = parseJSONToTyped(entry.value)
                    targetInstance."$entry.key" = typedSubObjects
                }
                else if (entry.key != "class") {
                    targetInstance."$entry.key" = entry.value
                }
            }

            targetInstance
        }

        return typedObjects
    }
4 голосов
/ 31 августа 2010

Если вы делаете это в контроллере Grails, а Foo IS действительно является доменным объектом, не забывайте, что, вооружившись картой JSON, вы также можете сделать:

List list = [new Foo("first"), new Foo("second")]
def jsonString = (list as JSON).toString()

List parsedList = JSON.parse(jsonString) as List
Foo foo = new Foo()
bindData(foo, parsedList[0]);
0 голосов
/ 03 февраля 2016

По состоянию на Grails 2.5 это возможно:

Period test = new Period()
test.periodText = 'test'
String j = test as JSON
def p = JSON.parse(j)
test = p.asType(Period)
println(test.periodText)

Выход:

test

Я не уверен, когда это стало возможным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...