Grails数据绑定:使用映射创建抽象类的实例

Grails数据绑定:使用映射创建抽象类的实例,grails,Grails,与我的上一个问题()类似,我想对一个包含具有hasMany关系的抽象类集合的类使用数据绑定,但在本例中,我使用的不是列表,而是映射 我创建了一个集成测试失败的smalll项目,以显示问题,运行时使用: grails test-app -integration -echoOut DataBinding 无论如何,我将在这里通过描述类和测试来解释这个问题: class LocalizableContent { Map contentByLocale = [:].withDefault { l

与我的上一个问题()类似,我想对一个包含具有hasMany关系的抽象类集合的类使用数据绑定,但在本例中,我使用的不是列表,而是映射

我创建了一个集成测试失败的smalll项目,以显示问题,运行时使用:

grails test-app -integration -echoOut DataBinding
无论如何,我将在这里通过描述类和测试来解释这个问题:

class LocalizableContent {
   Map contentByLocale = [:].withDefault { locale -> new Text() }
   static hasMany = [ contentByLocale : Content ]
}
abstract class Content { 
   static belongsTo = [ localizableContent : LocalizableContent ]
   static constraints = {
      localizableContent nullable:true
   }
}
class Text extends Content {
   String text
}
如您所见,我已经在使用withDefault技巧,但Grails/Spring显然没有调用它(我甚至尝试在默认闭包中抛出一个异常来验证代码是否未执行)

为了测试,我还创建了一个LocalizableContentController,它是空的。在所有这些情况下,以下集成测试失败:

    void testMapDatabinding() {
       def rawParams = [ 'contentByLocale[en].text': 'Content' ]
       def controller = new LocalizableContentController()
       controller.request.addParameters(rawParams)
       controller.request.setAttribute(GrailsApplicationAttributes.CONTROLLER, controller)
       def localizableContent = new LocalizableContent(controller.params)
       assert localizableContent?.contentByLocale['en']?.text == 'Content'
    }
它说localizableContent.contentByLocale是一个看起来像['en':null]的映射,因此显然数据绑定正在理解映射语法并尝试为'en'键创建一个条目。但是没有首先尝试获取该键的条目,因为没有调用withDefault

下面的一个测试测试withDefault工作正常,并且通过:

   void testMapByDefaultWithNoDatabinding() {
      assert new LocalizableContent().contentByLocale['en']?.getClass() == Text
   }
这里我缺少什么?

不过是一种模式,当您面对未知的
键时,它可以提供有效的
值。例如,考虑下面的用例:

def map = [:].withDefault{k-> 
    println k //Should print 'a'
    10
}

map.test = 32

assert map.test == 32
assert map.a == 10
它以未知键作为参数,您不能向它传递任何值,这是一种逻辑,因为它提供了一个默认值,而不是提供的值

在您的情况下,如果将值设置为
Text
,则数据绑定将起作用,如:

Map contentByLocale = [:].withDefault { locale -> 
    //locale is the key. 'en' in this case
    new Text(locale: locale, text: 'Content') 
}
如果将文本类定义为

class Text extends Content{
    String locale
    String text
}

我完全理解Groovy提供的withDefault技术:),事实上,我希望能够向它传递一个密钥,我只是不使用它,所以示例不会变得太复杂。LocalizableContent按区域设置存储内容,因此内容中没有区域设置,它只是在LocalizableContent中按区域设置索引。您提供的代码并没有真正改变任何东西,因为这里的问题是根本没有调用withDefault闭包(我猜Spring/Grails数据绑定在尝试插入新的条目之前并没有在映射中查找现有条目)。