如果我有一个简单的对象,例如
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我的一个实例,我希望输出是
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 }
注意:
metaClass
class
如果可以将JSON转换器的输出作为对象,则可以对其进行迭代并删除metaClass和class属性,然后将其添加到外部JSON对象中。
但是,据我所知,JSON转换器似乎仅提供“全有或全无”方法,并将其输出返回为String
我终于想出了如何使用来执行此操作JSONBuilder,这是代码
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属性的值:
Complex
object
{"object": { "nest2": { "p2": "val2", "p1": "val1" }, "nest1": { "p2": "val2", "p1": "val1" }, "name": null }}