Groovy + JsonSlurper 奇怪的行为
Groovy + JsonSlurper strange behaviour
我有以下代码来解析 JSON 文件:
@Override
Map<String, Configuration> parseJson() {
Object configurationFile = readConfigurationFile()
configurationFile.schemas.each { schemaProtectionInformation ->
processing(schemaProtectionInformation)
}
}
private Object readConfigurationFile() {
InputStream is = getClass().getClassLoader().getResourceAsStream("test.json")
BufferedReader reader = new BufferedReader(new InputStreamReader(is))
return new JsonSlurper().parse(reader)
}
要处理以下 JSON 文件:
{
"schemas": [
{
"name": "plan_pm_test",
"protectedDimensions": [
{
"name": "dActivityWbs",
"usedToSecureFactTable": true,
"aliasInFactTable": "PLAN_WBS",
"levels" : ["LEVEL_1_ID","LEVEL_2_ID","LEVEL_3_ID","LEVEL_4_ID","LEVEL_5_ID","LEVEL_6_ID","LEVEL_7_ID","LEVEL_8_ID","LEVEL_9_ID"]
},
{
"name": "dResponsibleOrganicUnit",
"usedToSecureFactTable": true,
"aliasInFactTable": "RES_ORG_UNIT",
"levels" : ["ID","LEVEL_1_ID","LEVEL_2_ID"]
},
{
"name": "dContributionOrganicUnit",
"usedToSecureFactTable": true,
"aliasInFactTable": "CON_ORG_UNIT",
"levels" : ["ID","LEVEL_1_ID","LEVEL_2_ID"]
}
]
}
]
}
如果我执行这段代码,我将收到以下错误:
Cannot cast object '[{name=plan_pm_test, protectedDimensions=[{aliasInFactTable=PLAN_WBS, levels=[LEVEL_1_ID, LEVEL_2_ID, LEVEL_3_ID, LEVEL_4_ID, LEVEL_5_ID, LEVEL_6_ID, LEVEL_7_ID, LEVEL_8_ID, LEVEL_9_ID], name=dActivityWbs, usedToSecureFactTable=true}, {aliasInFactTable=RES_ORG_UNIT, levels=[ID, LEVEL_1_ID, LEVEL_2_ID], name=dResponsibleOrganicUnit, usedToSecureFactTable=true}, {aliasInFactTable=CON_ORG_UNIT, levels=[ID, LEVEL_1_ID, LEVEL_2_ID], name=dContributionOrganicUnit, usedToSecureFactTable=true}]}]' with class 'java.util.ArrayList' to class 'java.util.Map' due to: groovy.lang.GroovyRuntimeException: Could not find matching constructor for: java.util.Map(groovy.json.internal.LazyMap)
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '[{name=plan_pm_test, protectedDimensions=[{aliasInFactTable=PLAN_WBS, levels=[LEVEL_1_ID, LEVEL_2_ID, LEVEL_3_ID, LEVEL_4_ID, LEVEL_5_ID, LEVEL_6_ID, LEVEL_7_ID, LEVEL_8_ID, LEVEL_9_ID], name=dActivityWbs, usedToSecureFactTable=true}, {aliasInFactTable=RES_ORG_UNIT, levels=[ID, LEVEL_1_ID, LEVEL_2_ID], name=dResponsibleOrganicUnit, usedToSecureFactTable=true}, {aliasInFactTable=CON_ORG_UNIT, levels=[ID, LEVEL_1_ID, LEVEL_2_ID], name=dContributionOrganicUnit, usedToSecureFactTable=true}]}]' with class 'java.util.ArrayList' to class 'java.util.Map' due to: groovy.lang.GroovyRuntimeException: Could not find matching constructor for: java.util.Map(groovy.json.internal.LazyMap)
at cern.ais.datawarehouse.baserver.mondriansecurity.common.schemaprotectionconfiguration.JsonResourceFileConfigurationRepositoryPopulator.readConfiguration(JsonResourceFileConfigurationRepositoryPopulator.groovy:23)
at cern.ais.datawarehouse.baserver.mondriansecurity.common.schemaprotectionconfiguration.JsonResourceFileConfigurationRepositoryPopulatorTest.tes(JsonResourceFileConfigurationRepositoryPopulatorTest.groovy:12)
所以我当然开始一步步调试应用程序,看processing()部分的哪部分代码抛出这个异常。令人惊讶的是那里的所有代码都正常执行:没有抛出异常并返回结果 I except.
更让我吃惊的是,当我稍微改变第一个方法的代码时,它没有产生异常。
@Override
Map<String, Configuration> readConfiguration() {
Object configurationFile = readConfigurationFile()
configurationFile.schemas.each { schemaProtectionInformation ->
processing(schemaProtectionInformation)
}
println "test 2"
}
我不知道 println 方法如何改变那里的任何东西。当然,不一定非要 println 方法才能发挥作用。所以如果我这样做:
@Override
Map<String, Configuration> readConfiguration() {
Object configurationFile = readConfigurationFile()
configurationFile.schemas.each { schemaProtectionInformation ->
processing(schemaProtectionInformation)
}
test()
}
void test() {
}
它也会起作用(不会抛出异常)。我不知道为什么在处理 json 文件后有一些额外的代码应该在这里进行任何更改。
刚才其实把处理方法注释掉了,所以方法体如下图
@Override
Map<String, Configuration> readConfiguration() {
Object configurationFile = readConfigurationFile()
configurationFile.schemas.each { schemaProtectionInformation ->
//processing(schemaProtectionInformation)
}
}
即使我收到同样的异常。因此,错误与处理方法的实现无关。
非常感谢您的意见。
哇,对不起。这样的菜鸟错误。太糟糕了,我没有对此 class 进行单元测试,因为我会更快地发现缺失的位。
显然缺少的部分是 return 关键字。现在的代码如下所示:
@Override
Map<String, Configuration> readConfiguration() {
Object configurationFile = readConfigurationFile()
configurationFile.schemas.each() { schemaProtectionInformation ->
processSchemaDetailsFromFile(schemaProtectionInformation)
}
return schemasConfigurations
}
并且工作没有任何问题。
如果我没记错的话,这段代码是从 reloadConfiguration() 没有 return 值的代码演变而来的。然后可能我更改了 return 类型但忘记添加显式 return 语句。由于 groovy 允许没有 return 关键字,因此它没有抱怨并尝试 return 一些列表然后失败导致指定类型的值被此方法 returned是地图。
嗯...怪睡眠不足
在Groovy中,return
是隐含的,它是函数的最后一条语句。所以你的代码相当于:
@Override
Map<String, Configuration> parseJson() {
Object configurationFile = readConfigurationFile()
return configurationFile.schemas.each { schemaProtectionInformation ->
processing(schemaProtectionInformation)
}
}
each
函数 return 调用的元素。在你的情况下,schemas
。但是,模式是一个集合,而不是一个映射:您会看到 ClassCastException。您的代码相当于:
@Override
Map<String, Configuration> parseJson() {
Object configurationFile = readConfigurationFile()
configurationFile.schemas.each { schemaProtectionInformation ->
processing(schemaProtectionInformation)
}
return configurationFile.schemas
}
当您在此语句之后添加一些内容时,您只是在创建另一个隐式 return
。您应该使用明确的 return configurationFile
.
我有以下代码来解析 JSON 文件:
@Override
Map<String, Configuration> parseJson() {
Object configurationFile = readConfigurationFile()
configurationFile.schemas.each { schemaProtectionInformation ->
processing(schemaProtectionInformation)
}
}
private Object readConfigurationFile() {
InputStream is = getClass().getClassLoader().getResourceAsStream("test.json")
BufferedReader reader = new BufferedReader(new InputStreamReader(is))
return new JsonSlurper().parse(reader)
}
要处理以下 JSON 文件:
{
"schemas": [
{
"name": "plan_pm_test",
"protectedDimensions": [
{
"name": "dActivityWbs",
"usedToSecureFactTable": true,
"aliasInFactTable": "PLAN_WBS",
"levels" : ["LEVEL_1_ID","LEVEL_2_ID","LEVEL_3_ID","LEVEL_4_ID","LEVEL_5_ID","LEVEL_6_ID","LEVEL_7_ID","LEVEL_8_ID","LEVEL_9_ID"]
},
{
"name": "dResponsibleOrganicUnit",
"usedToSecureFactTable": true,
"aliasInFactTable": "RES_ORG_UNIT",
"levels" : ["ID","LEVEL_1_ID","LEVEL_2_ID"]
},
{
"name": "dContributionOrganicUnit",
"usedToSecureFactTable": true,
"aliasInFactTable": "CON_ORG_UNIT",
"levels" : ["ID","LEVEL_1_ID","LEVEL_2_ID"]
}
]
}
]
}
如果我执行这段代码,我将收到以下错误:
Cannot cast object '[{name=plan_pm_test, protectedDimensions=[{aliasInFactTable=PLAN_WBS, levels=[LEVEL_1_ID, LEVEL_2_ID, LEVEL_3_ID, LEVEL_4_ID, LEVEL_5_ID, LEVEL_6_ID, LEVEL_7_ID, LEVEL_8_ID, LEVEL_9_ID], name=dActivityWbs, usedToSecureFactTable=true}, {aliasInFactTable=RES_ORG_UNIT, levels=[ID, LEVEL_1_ID, LEVEL_2_ID], name=dResponsibleOrganicUnit, usedToSecureFactTable=true}, {aliasInFactTable=CON_ORG_UNIT, levels=[ID, LEVEL_1_ID, LEVEL_2_ID], name=dContributionOrganicUnit, usedToSecureFactTable=true}]}]' with class 'java.util.ArrayList' to class 'java.util.Map' due to: groovy.lang.GroovyRuntimeException: Could not find matching constructor for: java.util.Map(groovy.json.internal.LazyMap)
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '[{name=plan_pm_test, protectedDimensions=[{aliasInFactTable=PLAN_WBS, levels=[LEVEL_1_ID, LEVEL_2_ID, LEVEL_3_ID, LEVEL_4_ID, LEVEL_5_ID, LEVEL_6_ID, LEVEL_7_ID, LEVEL_8_ID, LEVEL_9_ID], name=dActivityWbs, usedToSecureFactTable=true}, {aliasInFactTable=RES_ORG_UNIT, levels=[ID, LEVEL_1_ID, LEVEL_2_ID], name=dResponsibleOrganicUnit, usedToSecureFactTable=true}, {aliasInFactTable=CON_ORG_UNIT, levels=[ID, LEVEL_1_ID, LEVEL_2_ID], name=dContributionOrganicUnit, usedToSecureFactTable=true}]}]' with class 'java.util.ArrayList' to class 'java.util.Map' due to: groovy.lang.GroovyRuntimeException: Could not find matching constructor for: java.util.Map(groovy.json.internal.LazyMap)
at cern.ais.datawarehouse.baserver.mondriansecurity.common.schemaprotectionconfiguration.JsonResourceFileConfigurationRepositoryPopulator.readConfiguration(JsonResourceFileConfigurationRepositoryPopulator.groovy:23)
at cern.ais.datawarehouse.baserver.mondriansecurity.common.schemaprotectionconfiguration.JsonResourceFileConfigurationRepositoryPopulatorTest.tes(JsonResourceFileConfigurationRepositoryPopulatorTest.groovy:12)
所以我当然开始一步步调试应用程序,看processing()部分的哪部分代码抛出这个异常。令人惊讶的是那里的所有代码都正常执行:没有抛出异常并返回结果 I except.
更让我吃惊的是,当我稍微改变第一个方法的代码时,它没有产生异常。
@Override
Map<String, Configuration> readConfiguration() {
Object configurationFile = readConfigurationFile()
configurationFile.schemas.each { schemaProtectionInformation ->
processing(schemaProtectionInformation)
}
println "test 2"
}
我不知道 println 方法如何改变那里的任何东西。当然,不一定非要 println 方法才能发挥作用。所以如果我这样做:
@Override
Map<String, Configuration> readConfiguration() {
Object configurationFile = readConfigurationFile()
configurationFile.schemas.each { schemaProtectionInformation ->
processing(schemaProtectionInformation)
}
test()
}
void test() {
}
它也会起作用(不会抛出异常)。我不知道为什么在处理 json 文件后有一些额外的代码应该在这里进行任何更改。
刚才其实把处理方法注释掉了,所以方法体如下图
@Override
Map<String, Configuration> readConfiguration() {
Object configurationFile = readConfigurationFile()
configurationFile.schemas.each { schemaProtectionInformation ->
//processing(schemaProtectionInformation)
}
}
即使我收到同样的异常。因此,错误与处理方法的实现无关。
非常感谢您的意见。
哇,对不起。这样的菜鸟错误。太糟糕了,我没有对此 class 进行单元测试,因为我会更快地发现缺失的位。
显然缺少的部分是 return 关键字。现在的代码如下所示:
@Override
Map<String, Configuration> readConfiguration() {
Object configurationFile = readConfigurationFile()
configurationFile.schemas.each() { schemaProtectionInformation ->
processSchemaDetailsFromFile(schemaProtectionInformation)
}
return schemasConfigurations
}
并且工作没有任何问题。
如果我没记错的话,这段代码是从 reloadConfiguration() 没有 return 值的代码演变而来的。然后可能我更改了 return 类型但忘记添加显式 return 语句。由于 groovy 允许没有 return 关键字,因此它没有抱怨并尝试 return 一些列表然后失败导致指定类型的值被此方法 returned是地图。
嗯...怪睡眠不足
在Groovy中,return
是隐含的,它是函数的最后一条语句。所以你的代码相当于:
@Override
Map<String, Configuration> parseJson() {
Object configurationFile = readConfigurationFile()
return configurationFile.schemas.each { schemaProtectionInformation ->
processing(schemaProtectionInformation)
}
}
each
函数 return 调用的元素。在你的情况下,schemas
。但是,模式是一个集合,而不是一个映射:您会看到 ClassCastException。您的代码相当于:
@Override
Map<String, Configuration> parseJson() {
Object configurationFile = readConfigurationFile()
configurationFile.schemas.each { schemaProtectionInformation ->
processing(schemaProtectionInformation)
}
return configurationFile.schemas
}
当您在此语句之后添加一些内容时,您只是在创建另一个隐式 return
。您应该使用明确的 return configurationFile
.