简体   繁体   English

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

[英]Grails - how to let a domain class convert JSON into a domain property

I want to teach my domain class to automatically convert the results of JSON.parse(someJSON) into a member that is also a custom domain class. 我想教我的域类自动将JSON.parse(someJSON)的结果转换为也是自定义域类的成员。

Given these domain classes: 给定这些域类:

class Person {
    Long id
    String name

    static hasMany = [aliases: PersonAlias]
}

class PersonAlias {
    Person person
    Long id
    String name
}

And this JSON representing a Person with some PersonAliases: 并且此JSON表示具有某些PersonAliases的Person:

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

I want to keep the controller simple like: 我想保持控制器简单如:

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

But sadly I get this error: 但是可悲的是我得到了这个错误:

Failed to convert property value of type 'org.codehaus.groovy.grails.web.json.JSONArray' to required type 'java.util.Set' for property 'aliases'; 无法将类型“ org.codehaus.groovy.grails.web.json.JSONArray”的属性值转换为属性“ aliases”的必需类型“ java.util.Set”; nested exception is java.lang.IllegalStateException: Cannot convert value of type [org.codehaus.groovy.grails.web.json.JSONObject] to required type [heavymeta.PersonAlias] for property 'aliases[0]': no matching editors or conversion strategy found 嵌套异常为java.lang.IllegalStateException:无法将类型“ org.codehaus.groovy.grails.web.json.JSONObject]的值转换为属性“ aliases [0]”的必需类型[heavymeta.PersonAlias]:没有匹配的编辑器或找到转换策略

So, I want to teach my domain class to how to convert the JSON data into PersonAlias instances automatically. 因此,我想教我的域类如何将JSON数据自动转换为PersonAlias实例。 I'd like to avoid formatting the data in the controller before passing it to the Domain object. 我想避免在将数据传递给Domain对象之前格式化控制器中的数据。 How do I accomplish these goals? 我如何实现这些目标?

You can use the bindUsing annotation and provide your custom binding code to convert the json to the property being bound. 您可以使用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]
}

I think this plugin: https://github.com/pedjak/grails-marshallers might do what you're looking for? 我认为这个插件: https : //github.com/pedjak/grails-marshallers可能会满足您的需求? I have not tried it myself though. 我自己还没有尝试过。

I also encountered this problem - I did my best to document the fix on my website - See http://dalelotts.com/software-architect/grails 我也遇到了这个问题-我已尽力在网站上记录了此修复程序-请参阅http://dalelotts.com/software-architect/grails

In general the solution is to convert the JSON to a parameter map that can be used for data binding. 通常,解决方案是将JSON转换为可用于数据绑定的参数映射。 More info on the site, including an annotation driven DomainClassMarshaller for 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)
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM