Grails - 如何让域类将JSON转换为域属性

时间:2012-08-07 02:36:25

标签: json grails

我想教我的域类自动将JSON.parse(someJSON)的结果转换为也是自定义域类的成员。

鉴于这些域类:

class Person {
    Long id
    String name

    static hasMany = [aliases: PersonAlias]
}

class PersonAlias {
    Person person
    Long id
    String name
}

这个JSON代表一个具有一些PersonAliases的人:

{
     "id":20044397,
     "name":"John Smith",
     "aliases":[{"id":13376,"name":"Johnny Smith"},{"id":13377,"name":"J. Smith"}]
}

我想让控制器保持简单:

class PersonController {
    def saveViaAjax = {
        def props = JSON.parse(params.JSON)
        Person p = Person.get(props.id)
        p.properties = props
        p.save(flush: true)
    }
}

但遗憾的是我收到了这个错误:

  

无法转换类型的属性值   'org.codehaus.groovy.grails.web.json.JSONArray'到必需的类型   属性'别名'的'java.util.Set';嵌套异常是   java.lang.IllegalStateException:无法转换类型的值   [org.codehaus.groovy.grails.web.json.JSONObject]到必需的类型   [heavymeta.PersonAlias]属性'aliases [0]':没有匹配的编辑器   或发现转换策略

所以,我想教我的域类如何自动将JSON数据转换为PersonAlias实例。我想避免在将数据传递给Domain对象之前格式化控制器中的数据。我如何实现这些目标?

3 个答案:

答案 0 :(得分:1)

您可以使用bindUsing注释并提供自定义绑定代码,以将json转换为绑定的属性。

class Person {
    Long id
    String name

    @BindUsing({obj, source ->
       List retVal = [] 
       def aliases = source['aliases']
       if(aliases) {
           aliases.each { 
             retVal << new PersonAlias(name:it.name)
           }
       } 
       return retVal
    })
    List<PersonAlias> aliases

    static hasMany = [aliases: PersonAlias]
}

答案 1 :(得分:0)

我认为这个插件:https://github.com/pedjak/grails-marshallers可能正在寻找你想要的东西吗?我自己没试过。

答案 2 :(得分:0)

我也遇到过这个问题 - 我尽力记录我网站上的修复程序 - 请参阅http://dalelotts.com/software-architect/grails

通常,解决方案是将JSON转换为可用于数据绑定的参数映射。有关该网站的更多信息,包括用于JSON的注释驱动的DomainClassMarshaller

protected Object readFromJson(Class type, InputStream entityStream, String charset) {

   def mapper = new ObjectMapper();
   def parsedJSON = mapper.readValue(entityStream, typeRef);

   Map<String, Object> map = new HashMap<>();

   parsedJSON.entrySet().each {Map.Entry<String, Object> entry ->
       if (List.isAssignableFrom(entry.getValue().getClass())) {

           List values = (List) entry.getValue();
           int limit = values.size()
           for (int i = 0; i <  limit; i++) {
               final theValue = values.get(i)
               map.put(entry.key + '[' + i + ']', theValue)

               appendMapValues(map, theValue, entry.key + '[' + i + ']' )

           }
       } else {
           map.put(entry.key, entry.value);
       }
   }

   def result = type.metaClass.invokeConstructor(map)


   // Workaround for http://jira.codehaus.org/browse/GRAILS-1984
   if (!result.id) {
       result.id = idFromMap(map)
   }
   result
}

private void appendMapValues(Map<String, Object> theMap, Object theValue, String prefix) {
    if (Map.isAssignableFrom(theValue.getClass())) {

        Map<String, Object> valueMap = (Map<String, Object>) theValue;

        for (Map.Entry<String, Object> valueEntry : valueMap.entrySet()) {
            theMap.put(prefix + '.' + valueEntry.key, valueEntry.value)
            appendMapValues(theMap, valueEntry.value, prefix + '.' + valueEntry.key)
        }
    }
}