Grails JSON 数组

Posted

技术标签:

【中文标题】Grails JSON 数组【英文标题】:Grails JSON array 【发布时间】:2011-02-09 22:22:14 【问题描述】:

我正在将 Foo 对象列表转换为 JSON 字符串。我需要将 JSON 字符串解析回 Foos 列表。然而在下面的例子中,解析给了我一个 JSONObjects 列表而不是 Foos。

示例

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 呢? 提前致谢。

【问题讨论】:

【参考方案1】:

我查看了 JSON 的 API 文档,似乎没有任何方法可以将 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"))

更通用的解决方案是向JSON 元类添加一个新的静态parse 方法,例如以下方法,该方法尝试将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'] 

您可以在 groovy 控制台中试用上面的代码。一些警告

我只对上面的代码进行了非常有限的测试 最新的 Grails 版本中有两个 JSON 类,我假设您使用的是未弃用的一个

【讨论】:

是否可以将每个 jsonObj 目录的属性注入到每个新的 Foo 实例的 foo.properties 字段中? @Ali G - 不,我认为.properties 只对 Grails 域对象是可写的。对于常规的 Groovy 对象,我认为 .properties 是只读的。 谢谢唐。通用方法非常好。 公平点,唐 - 我猜我在那里做了一些重大假设! 如何处理日期和可为空的值?【参考方案2】:

如果您在 Grails 控制器中执行此操作,并且 Foo 确实是一个域对象,请不要忘记使用您的 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]);

【讨论】:

甚至更好 Foo foo = new Foo(parsedList[0])【参考方案3】:

我已采用此代码并将其扩展为使用嵌套结构。它依赖于 JSON 中存在的“类”属性。如果 Grails 现在有更好的方法,请告诉我。

     // 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】:

从 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

我不确定它何时成为一种选择。

【讨论】:

以上是关于Grails JSON 数组的主要内容,如果未能解决你的问题,请参考以下文章

在 Groovy/Grails 中使用 JSON 创建对象

Grails 中视图的 JSON 输出

Grails - 使用 JSON 启动域类

使用 Grails 在服务器端获取 JSON 数据

控制器中每个操作的 JSON Marshaller (Grails)

Grails - 将 UUID 呈现为 JSON