【问题标题】:SnakeYAML is instantiating ArrayList instead of HashMapSnakeYAML 正在实例化 ArrayList 而不是 HashMap
【发布时间】:2019-06-02 00:19:22
【问题描述】:

我需要解析以下 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(我不知道这里发生了什么魔法)。

这是调试窗口的快照:

我的测试类的输出:

{
  "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>

有人遇到过这个问题吗?我找不到解决办法。

【问题讨论】:

  • 如果只使用SetMap 接口会发生什么?指定这样的实现通常被认为是不好的做法(出于多种原因),并且使用该接口可能会修复该行为。
  • 我试过了,但还是不行

标签: java yaml snakeyaml


【解决方案1】:

你的问题大概是type erasure:

当类型安全(通用)集合是 JavaBean 属性时,SnakeYAML 会动态检测所需的类。 […]

如果泛型类型是抽象类(接口)则不起作用。 您必须在 YAML 中放置显式标记或提供显式 TypeDescription。 TypeDescription 的目的是收集更多信息并在加载/转储时使用它。

虽然您不使用抽象类或接口,但我认为 SnakeYaml 在发现 HashSet&lt;HashMap&lt;String, HashSet&gt;&gt; 的嵌套泛型类型方面存在问题。文档建议添加 TypeDescription;但是,这并不能解决您的问题,因为接口的设计使您只能在外部HashSet 内指定类型,而不能在内部HashMap 内指定类型。接口不期望嵌套容器的事实也暗示这是您的问题。

解决方法是将 YAML 中的显式标签添加到无法正确加载的集合中:

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

如果您不想这样做,您基本上还有两个选择:将此功能修补到 SnakeYAML,或使用低级 API 并从解析器事件手动生成您的类型。

【讨论】:

  • 我去看看
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-06
  • 1970-01-01
  • 2022-06-12
  • 1970-01-01
  • 2017-01-16
  • 1970-01-01
相关资源
最近更新 更多