Testing Groovy/Grails的扩散\&引用;字符串中的转义字符

Testing Groovy/Grails的扩散\&引用;字符串中的转义字符,testing,grails,groovy,Testing,Grails,Groovy,这是一个相当奇怪的问题。我正在集成测试Grails服务和关联的域类。该域类的一个属性是包含JSON的字符串。数据库字段也是json,并且有一个自定义的Hibernate值类型来执行必要的转换。它已经在另一个领域类的生产中工作了多年 class MyDomain { String data static mapping = { data type: StringJsonUserType } } 到目前为止还不错。在我的测试中,我将模拟一个输入对象到我的服务

这是一个相当奇怪的问题。我正在集成测试Grails服务和关联的域类。该域类的一个属性是包含JSON的
字符串。数据库字段也是
json
,并且有一个自定义的Hibernate值类型来执行必要的转换。它已经在另一个领域类的生产中工作了多年

class MyDomain {
    String data
    static mapping = {
        data type: StringJsonUserType
    }
}
到目前为止还不错。在我的测试中,我将模拟一个输入对象到我的服务方法中,它最终将包含并返回所需的JSON字符串

private MockedClass mockClass() {
    // JsonRepresentable declares asJson() method.
    def data = GroovyMock(JsonRepresentable)
    data.asJson() >> "{\"content\":\"irrelevant\"}"

    def mockClass = GroovyMock(MockedClass)
    mockClass.getData() >> data

    return mockClass
}
服务方法(简化):

当我使用调试器进入这段代码时,我可以立即看到字符串已从
字符串
变量中的
{“content”:“unrelated”}
变为
变量中的
“{content\”:\“unrelated\”}

现在唯一合乎逻辑的是,在我的测试中,保存的域类字符串的比较与模拟输入不匹配

这是从数据库读取数据时的
MyDomain.data
data的外观:

“\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/code>

这是使用
new JsonSlurper()解析的相同字符串。parseText(MyDomain.data)

“\”{\\\\\“内容\\”:\\\\“无关\\”}\”

下面是用JsonSlurper解析的模拟字符串(如上所述):

[内容:无关]

显然,最后一个例子就是我所期望的。有人能告诉我为什么Groovy/Grails会在我的简单且正确转义的字符串中添加大量crapy\\s吗?我甚至可以尝试使用Groovy字符串
'{“content”:“uncertible”}
,但这没有任何区别。

只是出于偶然(在查找重命名包后出现的一些其他奇怪问题时),我发现了问题的原因。在我的域类中,我不仅有
String
属性,还有从该字符串返回JSON对象或接受JSON对象并将其转换为字符串的临时getter和setter

class MyDomain {
    String data
    static mapping = {
        data type: StringJsonUserType
    }

    static transients = ['dataJson']

    def getDataJson() {
        return new JsonSlurper().parseText(data)
    }

    void setDataJson(def data) {
        data = JsonOutput.toJson(data)
    }
}
不幸的是,我在
setDataJson
中输入了一个错误。它的名称是
setData
,因此它被用作服务方法中我的
字符串的setter

void persist(MockedClass mock) {
    String string = mock.data.asJson()
    def domain = new MyDomain(data: mock.data.asJson())
    domain.save()
}
这意味着
JsonOutput.toJson(data)
将我的JSON字符串转换为另一个JSON字符串,这就是所有附加转义字符的来源

故事的寓意:切换到正确编译的语言,在编译时强制执行类型系统

void persist(MockedClass mock) {
    String string = mock.data.asJson()
    def domain = new MyDomain(data: mock.data.asJson())
    domain.save()
}