Grails 5 DeepDomainClassMarshaller处理JSON循环引用与空值问题
Grails 5 领域类转JSON系列问题解决方案
环境配置
grailsVersion=5.3.2 grailsGradlePluginVersion=5.3.0 groovyVersion=3.0.11 gorm.version=7.3.3
涉及领域类:Company、User、ZApp(类间存在双向关联)
问题1:深度转换触发循环引用错误
配置:设置grails.converters.json.default.deep = true
异常信息:
Caused by: org.grails.web.converters.exceptions.ConverterException: java.lang.StringIndexOutOfBoundsException: start 0, end -1, length 0
原因:领域类双向关联导致JSON序列化时出现循环引用,默认逻辑无法处理。
解决:添加配置启用循环引用的PATH处理:
grails.converters.json.circular.reference.behaviour='PATH'
问题2:空值字段触发JSON序列错误
异常信息:
Caused by: org.grails.web.converters.exceptions.ConverterException: org.grails.web.json.JSONException: Value out of sequence: expected mode to be OBJECT or ARRAY when writing 'org.grails.web.json.JSONWriter$NullWritable@1a1fd03d' but was INIT
原因:PATH模式下的JSONWriter对空值的处理逻辑存在兼容性问题,直接输出空值违反了JSON写入状态。
解决:自定义DomainMarshaller显式处理空值:
import grails.converters.JSON import org.grails.web.json.JSONWriter class CustomDomainMarshaller implements JSONMarshaller { @Override void marshalObject(Object o, JSON json) { JSONWriter writer = json.getWriter() writer.object() o.class.declaredFields.each { field -> field.accessible = true def value = field.get(o) writer.key(field.name) if (value == null) { writer.value(null) } else { json.convertAnother(value) } } writer.endObject() } }
在BootStrap.groovy注册Marshaller:
JSON.registerMarshaller(Company, new CustomDomainMarshaller()) JSON.registerMarshaller(User, new CustomDomainMarshaller()) JSON.registerMarshaller(ZApp, new CustomDomainMarshaller())
问题3:HasMany关联触发类型转换异常
异常信息:
Caused by: java.lang.ClassCastException: class org.grails.web.json.PathCapturingJSONWriterWrapper$PropertyElement cannot be cast to class org.grails.web.json.PathCapturingJSONWriterWrapper$IndexElement
触发条件:Company的apps(hasMany关联)包含2个及以上元素时触发,返回JSON格式异常。
原因:自定义Marshaller未适配PATH模式下PathCapturingJSONWriterWrapper的状态管理,集合序列化时错误将属性元素当作索引元素处理。
最终解决方案:
推荐方案:改用
IGNORE模式处理循环引用,避免PATH模式的兼容问题:grails.converters.json.circular.reference.behaviour='IGNORE'该模式会自动忽略循环引用的字段,稳定性更高。
保留PATH模式的修复:修改自定义Marshaller,显式处理集合类型:
@Override void marshalObject(Object o, JSON json) { JSONWriter writer = json.getWriter() writer.object() o.class.declaredFields.each { field -> field.accessible = true def value = field.get(o) writer.key(field.name) if (value == null) { writer.value(null) } else if (value instanceof Collection) { writer.array() value.each { item -> json.convertAnother(item) } writer.endArray() } else { json.convertAnother(value) } } writer.endObject() }通过显式开启/结束数组,确保JSONWriter状态正确。
替代方案:使用
grails-datastore-rest插件,它对关联和空值的处理更健壮:
在build.gradle引入插件:implementation "org.grails.plugins:grails-datastore-rest:7.3.3"可通过领域类静态映射配置序列化规则:
class Company { static mapping = { json include: ['name', 'agentId', 'apps'], deep: true } }
内容的提问来源于stack exchange,提问作者Chetan Sharma
相关产品推荐
相关产品推荐

