Странное поведение Groovy + JsonSlurper

У меня есть следующий код для анализа файла 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
Cannot cast object '[{name=plan_pm_test, protectedDimensions=[{aliasInFactTable=PLAN_WBS, levels=[LEVEL_1_ID, LEVEL_2_ID, LEVEL_3_ID, LEVEL
@Override
Map<String, Configuration> readConfiguration() {
    Object configurationFile = readConfigurationFile()
    configurationFile.schemas.each { schemaProtectionInformation ->
        processing(schemaProtectionInformation)
    }
    println "test 2"
}
ID, LEVEL
@Override
Map<String, Configuration> readConfiguration() {
    Object configurationFile = readConfigurationFile()
    configurationFile.schemas.each { schemaProtectionInformation ->
        processing(schemaProtectionInformation)
    }
    test()
}

void test() {

}
ID, LEVEL
@Override
Map<String, Configuration> readConfiguration() {
    Object configurationFile = readConfigurationFile()
    configurationFile.schemas.each { schemaProtectionInformation ->
        //processing(schemaProtectionInformation)
    }
}
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
@Override
Map<String, Configuration> readConfiguration() {
    Object configurationFile = readConfigurationFile()
    configurationFile.schemas.each { schemaProtectionInformation ->
        processing(schemaProtectionInformation)
    }
    println "test 2"
}
ID, LEVEL
@Override
Map<String, Configuration> readConfiguration() {
    Object configurationFile = readConfigurationFile()
    configurationFile.schemas.each { schemaProtectionInformation ->
        processing(schemaProtectionInformation)
    }
    test()
}

void test() {

}
ID, LEVEL
@Override
Map<String, Configuration> readConfiguration() {
    Object configurationFile = readConfigurationFile()
    configurationFile.schemas.each { schemaProtectionInformation ->
        //processing(schemaProtectionInformation)
    }
}
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)
ID","LEVEL
@Override
Map<String, Configuration> readConfiguration() {
    Object configurationFile = readConfigurationFile()
    configurationFile.schemas.each { schemaProtectionInformation ->
        processing(schemaProtectionInformation)
    }
    println "test 2"
}
ID","LEVEL
@Override
Map<String, Configuration> readConfiguration() {
    Object configurationFile = readConfigurationFile()
    configurationFile.schemas.each { schemaProtectionInformation ->
        processing(schemaProtectionInformation)
    }
    test()
}

void test() {

}
ID","LEVEL
@Override
Map<String, Configuration> readConfiguration() {
    Object configurationFile = readConfigurationFile()
    configurationFile.schemas.each { schemaProtectionInformation ->
        //processing(schemaProtectionInformation)
    }
}
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
@Override
Map<String, Configuration> readConfiguration() {
    Object configurationFile = readConfigurationFile()
    configurationFile.schemas.each { schemaProtectionInformation ->
        processing(schemaProtectionInformation)
    }
    println "test 2"
}
ID, LEVEL
@Override
Map<String, Configuration> readConfiguration() {
    Object configurationFile = readConfigurationFile()
    configurationFile.schemas.each { schemaProtectionInformation ->
        processing(schemaProtectionInformation)
    }
    test()
}

void test() {

}
ID, LEVEL
@Override
Map<String, Configuration> readConfiguration() {
    Object configurationFile = readConfigurationFile()
    configurationFile.schemas.each { schemaProtectionInformation ->
        //processing(schemaProtectionInformation)
    }
}
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
@Override
Map<String, Configuration> readConfiguration() {
    Object configurationFile = readConfigurationFile()
    configurationFile.schemas.each { schemaProtectionInformation ->
        processing(schemaProtectionInformation)
    }
    println "test 2"
}
ID, LEVEL
@Override
Map<String, Configuration> readConfiguration() {
    Object configurationFile = readConfigurationFile()
    configurationFile.schemas.each { schemaProtectionInformation ->
        processing(schemaProtectionInformation)
    }
    test()
}

void test() {

}
ID, LEVEL
@Override
Map<String, Configuration> readConfiguration() {
    Object configurationFile = readConfigurationFile()
    configurationFile.schemas.each { schemaProtectionInformation ->
        //processing(schemaProtectionInformation)
    }
}
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() вызывает это исключение. На удивление весь код там выполняется нормально: без выбрасывания исключения и возврата результатов за исключением.

Что меня еще больше удивило, так это то, что когда я немного изменил код первого метода, он работает, не создавая исключения.

@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)
    }
}

И хотя я получаю то же исключение. Следовательно, ошибка не связана с реализацией метода обработки.

Я был бы очень признателен за ваш вклад.


person wooki    schedule 04.03.2016    source источник
comment
Это может помочь: stackoverflow.com/questions/41305311/ HashMap map = (HashMap) jsonSlurperClassic.parseText(record.getRecord());   -  person CodeRunner    schedule 27.05.2021


Ответы (2)


В Groovy return является неявным, это последний оператор функции. Итак, ваш код эквивалентен:

@Override
Map<String, Configuration> parseJson() {
    Object configurationFile = readConfigurationFile()
    return configurationFile.schemas.each { schemaProtectionInformation ->
        processing(schemaProtectionInformation)
    }
}

Функция each возвращает элемент, для которого вызывается. В вашем случае schemas. Однако схема — это коллекция, а не карта: вы видите ClassCastException. Ваш код эквивалентен:

@Override
Map<String, Configuration> parseJson() {
    Object configurationFile = readConfigurationFile()
    configurationFile.schemas.each { schemaProtectionInformation ->
        processing(schemaProtectionInformation)
    }
    return configurationFile.schemas
}

Когда вы добавляете что-то после этого оператора, вы просто создаете еще один неявный return. Вы должны использовать явный return configurationFile.

person Jérémie B    schedule 04.03.2016
comment
Кажется, мы ответили одновременно. Я понял ошибку через несколько минут после публикации вопроса. Тем не менее большое спасибо за помощь. - person wooki; 05.03.2016

Вау, извини. Такая ошибка новичка. Жаль, что у меня не было юнит-тестов для этого класса, потому что тогда я бы быстрее обнаружил недостающий бит.

Очевидно, недостающая часть — ключевое слово return. Код прямо сейчас выглядит так:

@Override
Map<String, Configuration> readConfiguration() {
    Object configurationFile = readConfigurationFile()
    configurationFile.schemas.each() { schemaProtectionInformation ->
        processSchemaDetailsFromFile(schemaProtectionInformation)
    }
    return schemasConfigurations
}

И работает без нареканий.

Если мне не изменяет память, этот код произошел от кода, в котором reloadConfiguration() не возвращала значение. Тогда, вероятно, я изменил тип возврата, но забыл добавить явный оператор возврата. Поскольку groovy позволяет не иметь возвращаемого ключевого слова, он не жаловался и пытался вернуть некоторый список, а затем потерпел неудачу, потому что указанный тип значения, возвращаемый этим методом, был map.

Ну... Я виню в этом недостаток сна.

person wooki    schedule 04.03.2016