Reputation: 448
I have following code to parse a JSON file:
@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)
}
To process following JSON file:
{
"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"]
}
]
}
]
}
If I execute this code I will receive following error:
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)
So of course I started debugging application step by step to see which part of code in part processing() throws this exception. Surprisingly all the code there executes normally: without throwing exception and returning results I except.
What suprised me even more is that when I changed slightly the code of first method, it works without producing the exception.
@Override
Map<String, Configuration> readConfiguration() {
Object configurationFile = readConfigurationFile()
configurationFile.schemas.each { schemaProtectionInformation ->
processing(schemaProtectionInformation)
}
println "test 2"
}
I have no idea how the println method can change anything there. Of course it does not have to be necessarily println method that does the trick. So if I do something like this:
@Override
Map<String, Configuration> readConfiguration() {
Object configurationFile = readConfigurationFile()
configurationFile.schemas.each { schemaProtectionInformation ->
processing(schemaProtectionInformation)
}
test()
}
void test() {
}
It will work as well (no expcetion thrown). I have no idea why having some additional code after processing the json file should make any change here.
Just now I have actually commented out the processing method, so that the method body looks like below.
@Override
Map<String, Configuration> readConfiguration() {
Object configurationFile = readConfigurationFile()
configurationFile.schemas.each { schemaProtectionInformation ->
//processing(schemaProtectionInformation)
}
}
And even though I receive the same exception. Hence, the error is not related to the implementation of processing method.
I would greatly appreciate your input.
Upvotes: 2
Views: 3050
Reputation: 11022
In Groovy, the return
is implicit, it's the last statement of a function. So your code is equivalent to :
@Override
Map<String, Configuration> parseJson() {
Object configurationFile = readConfigurationFile()
return configurationFile.schemas.each { schemaProtectionInformation ->
processing(schemaProtectionInformation)
}
}
The each
function return the element on which is called. In your case, schemas
. However, schema is a collection, not a map: You see the ClassCastException. Your code is equivalent to :
@Override
Map<String, Configuration> parseJson() {
Object configurationFile = readConfigurationFile()
configurationFile.schemas.each { schemaProtectionInformation ->
processing(schemaProtectionInformation)
}
return configurationFile.schemas
}
When you add something after this statement, you are just creating another implicit return
. You should use an explicit return configurationFile
.
Upvotes: 1
Reputation: 448
Wow, sorry. Such a rookie mistake. Too bad I did not have unit tests for this class, cause I would spot the missing bit faster then.
Obviously the missing part is a return keyword. The code right now looks like this:
@Override
Map<String, Configuration> readConfiguration() {
Object configurationFile = readConfigurationFile()
configurationFile.schemas.each() { schemaProtectionInformation ->
processSchemaDetailsFromFile(schemaProtectionInformation)
}
return schemasConfigurations
}
And works without any issues.
If my memory serves me right, this code evolved from one where reloadConfiguration() did not return a value. Then probably I changed the return type but forgot to add an explicit return statement. Since groovy allows not having a return keyword it did not complain and tried to return some list and then failed cause the specified type of value to be returned by this method was map.
Well... I blame lack of sleep.
Upvotes: 0