SnakeYAML создает экземпляр ArrayList вместо HashMap - PullRequest
1 голос
/ 02 июня 2019

Мне нужно проанализировать следующий файл YAML.

arguments:
  - Database
  - Fold
  - MetaFeature
  - Algorithm
  - Config
processes:
  - id: MetaFeatureCalculator
    command: "python metaFeatCalc.py {Database} folds/{Fold} de/{MetaFeature}/{Algorithm}.csv"
    in: [Database, Fold]
    out: [MetaFeature, Algorithm]
    log: "mf/{Fold}/{MetaFeature}.out"
  - id: Tunner
    command: "java -jar tunner.jar {MetaFeature} alg/{Algorithm} {config}"
    in: [Metafeature, Algorithm]
    out: [Config]
    log: "mf/{Metafeature}/{Algorithm}.out"
recipeDefaults:
  - Database: ["D1"]
recipes:
  - id: Ex1
    uses:
      - Database: ["D1", "D2"]
      - MetaFeature: ["M1", "M2"]
      - Algorithm: ["A1", "A2"]
      - Config: ["C1", "C4"]
  - id: Ex2
    uses:
      - Folds: ["F1", "F2", "F5"]
      - MetaFeature: ["M1", "M2"]
      - Algorithm: ["A1", "A2"]
      - Config: ["C1", "C4"]

И я создал следующие POJO для получения этих данных.

Репо: https://github.com/Pacheco95/ExperimentLoader

@Data
public class Experiment {
  private HashSet<String> arguments;
  private HashSet<Process> processes;
  private HashSet<HashMap<String, HashSet<String>>> recipeDefaults;
  private HashSet<Recipe> recipes;
}
@Data
public class Process {
  private String id;
  private String command;
  private HashSet<String> in;
  private HashSet<String> out;
  private String log;
}
@Data
public class Recipe {
  private String id;
  private HashSet<HashMap<String, HashSet>> uses;
}

И этот класс для проверки парсера:

public class ExperimentLoader {
  public static void main(String[] args) throws IOException {
    InputStream is = args.length == 0 ? System.in : Files.newInputStream(Paths.get(args[0]));
    Yaml yaml = new Yaml();
    Experiment experiment = yaml.loadAs(is, Experiment.class);
    Gson gson = new GsonBuilder().setPrettyPrinting().serializeNulls().create();
    System.out.println(gson.toJson(experiment));
  }
}

Парсер, кажется, работает хорошо, но при запуске этого кода в режиме отладки некоторые поля были созданы с правильным типом (HashSet)в то время как другие не делают.Они были созданы как ArrayLists (я понятия не имею, что за магия здесь произошла).

Это снимок окна удаления дубликатов:

Bug snapshot[1]

Вывод из моеготестовый класс:

{
  "arguments": [
    "Fold",
    "MetaFeature",
    "Config",
    "Database",
    "Algorithm"
  ],
  "processes": [
    {
      "id": "MetaFeatureCalculator",
      "command": "python metaFeatCalc.py {Database} folds/{Fold} de/{MetaFeature}/{Algorithm}.csv",
      "in": [
        "Fold",
        "Database"
      ],
      "out": [
        "MetaFeature",
        "Algorithm"
      ],
      "log": "mf/{Fold}/{MetaFeature}.out"
    },
    {
      "id": "Tunner",
      "command": "java -jar tunner.jar {MetaFeature} alg/{Algorithm} {config}",
      "in": [
        "Metafeature",
        "Algorithm"
      ],
      "out": [
        "Config"
      ],
      "log": "mf/{Metafeature}/{Algorithm}.out"
    }
  ],
  "recipeDefaults": [
    {
      "Database": [
        "D1"
      ]
    }
  ],
  "recipes": [
    {
      "id": "Ex2",
      "uses": [
        {
          "MetaFeature": [
            "M1",
            "M2"
          ]
        },
        {
          "Folds": [
            "F1",
            "F2",
            "F5"
          ]
        },
        {
          "Config": [
            "C1",
            "C4"
          ]
        },
        {
          "Algorithm": [
            "A1",
            "A2"
          ]
        }
      ]
    },
    {
      "id": "Ex1",
      "uses": [
        {
          "MetaFeature": [
            "M1",
            "M2"
          ]
        },
        {
          "Config": [
            "C1",
            "C4"
          ]
        },
        {
          "Database": [
            "D1",
            "D2"
          ]
        },
        {
          "Algorithm": [
            "A1",
            "A2"
          ]
        }
      ]
    }
  ]
}

Мои зависимости:

<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.8</version>
    <scope>provided</scope>
  </dependency>
  <dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.24</version>
  </dependency>
  <dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.5</version>
  </dependency>
</dependencies>

У кого-нибудь была эта проблема?Я не могу найти решение.

1 Ответ

1 голос
/ 03 июня 2019

Ваша проблема, вероятно, стирание типа :

Когда типобезопасные (универсальные) коллекции являются свойствами JavaBean, SnakeYAML динамически обнаруживает требуемый класс.[…]

Не работает, если универсальный тип является абстрактным классом (интерфейсом). Вы должны поместить явный тег в YAML или предоставить явное описание типа.TypeDescription служит для сбора дополнительной информации и ее использования во время загрузки / выгрузки.

Хотя вы не используете абстрактные классы или интерфейсы, я предполагаю, что у SnakeYaml есть проблема с обнаружением вложенных универсальных типов HashSet<HashMap<String, HashSet>>.Документация предлагает добавить TypeDescription;однако это не решит вашу проблему, потому что интерфейс спроектирован так, что вы можете указать только тип внутри внешнего HashSet, но не внутри HashMap.Тот факт, что интерфейс не ожидает вложенных контейнеров, также намекает на то, что это ваша проблема.

Обходным путем будет добавление явных тегов внутри YAML в наборы, которые не загружаются должным образом:

- Database: !!set ["D1"]
- MetaFeature: !!set ["M1", "M2"]

Если вы не хотите этого делать, у вас есть два других варианта: установить эту функцию в SnakeYAML или использовать низкоуровневый API и генерировать типы вручную из событий анализатора.

...