如果我有一个简单的对象,例如
class Person {
String name
Integer age
}
我可以使用JSONBuilder轻松将其用户定义的属性呈现为JSON
def person = new Person(name: 'bob', age: 22)
def builder = new JSONBuilder.build {
person.properties.each {propName, propValue ->
if (!['class', 'metaClass'].contains(propName)) {
// It seems "propName = propValue" doesn't work when propName is dynamic so we need to
// set the property on the builder using this syntax instead
setProperty(propName, propValue)
}
}
def json = builder.toString()
当属性很简单(即数字或字符串)时,这可以很好地工作。但是对于更复杂的对象,例如
class ComplexPerson {
Name name
Integer age
Address address
}
class Name {
String first
String second
}
class Address {
Integer houseNumber
String streetName
String country
}
有没有一种方法可以遍历整个对象图,将每个用户定义的属性以适当的嵌套级别添加到JSONBuilder?
换句话说,对于ComplexPerson
我的一个实例,我希望输出是
{
name: {
first: 'john',
second: 'doe'
},
age: 20,
address: {
houseNumber: 123,
streetName: 'Evergreen Terrace',
country: 'Iraq'
}
}
我认为我无法使用Grails JSON转换器执行此操作,因为我返回的实际JSON结构看起来像
{ status: false,
message: "some message",
object: // JSON for person goes here
}
注意:
ComplexPerson
是较大JSON对象的元素metaClass
andclass
如果可以将JSON转换器的输出作为对象,则可以对其进行迭代并删除metaClass
和class
属性,然后将其添加到外部JSON对象中。
但是,据我所知,JSON转换器似乎仅提供“全有或全无”方法,并将其输出返回为String
我终于想出了如何使用来执行此操作JSONBuilder
,这是代码
import grails.web.*
class JSONSerializer {
def target
String getJSON() {
Closure jsonFormat = {
object = {
// Set the delegate of buildJSON to ensure that missing methods called thereby are routed to the JSONBuilder
buildJSON.delegate = delegate
buildJSON(target)
}
}
def json = new JSONBuilder().build(jsonFormat)
return json.toString(true)
}
private buildJSON = {obj ->
obj.properties.each {propName, propValue ->
if (!['class', 'metaClass'].contains(propName)) {
if (isSimple(propValue)) {
// It seems "propName = propValue" doesn't work when propName is dynamic so we need to
// set the property on the builder using this syntax instead
setProperty(propName, propValue)
} else {
// create a nested JSON object and recursively call this function to serialize it
Closure nestedObject = {
buildJSON(propValue)
}
setProperty(propName, nestedObject)
}
}
}
}
/**
* A simple object is one that can be set directly as the value of a JSON property, examples include strings,
* numbers, booleans, etc.
*
* @param propValue
* @return
*/
private boolean isSimple(propValue) {
// This is a bit simplistic as an object might very well be Serializable but have properties that we want
// to render in JSON as a nested object. If we run into this issue, replace the test below with an test
// for whether propValue is an instanceof Number, String, Boolean, Char, etc.
propValue instanceof Serializable || propValue == null
}
}
您可以通过将上面的代码和以下代码粘贴到 grails 控制台中进行测试
// Define a class we'll use to test the builder
class Complex {
String name
def nest2 = new Expando(p1: 'val1', p2: 'val2')
def nest1 = new Expando(p1: 'val1', p2: 'val2')
}
// test the class
new JSONSerializer(target: new Complex()).getJSON()
它应生成以下输出,该输出将的序列化实例存储Complex
为object
属性的值:
{"object": {
"nest2": {
"p2": "val2",
"p1": "val1"
},
"nest1": {
"p2": "val2",
"p1": "val1"
},
"name": null
}}