Java 使用snakeYaml解析根上具有映射的YAML文档

Java 使用snakeYaml解析根上具有映射的YAML文档,java,yaml,snakeyaml,Java,Yaml,Snakeyaml,我想将YAML文档读入自定义对象的映射(而不是映射,snakeYaml默认会这样做)。因此: 19: typeID: 2 limit: 300 20: typeID: 8 limit: 100 将加载到如下所示的地图: Map 其中项目为: 类项目{ 私有整数类型ID; 私有整数限制; } 我找不到用snakeYaml实现这一点的方法,也找不到更好的库来完成这项任务 文档中只有嵌套在其他对象中的贴图/集合示例,因此您可以执行以下操作: TypeDescription Typ

我想将YAML文档读入自定义对象的映射(而不是映射,snakeYaml默认会这样做)。因此:

19:
  typeID: 2
  limit: 300
20:
  typeID: 8
  limit: 100
将加载到如下所示的地图:

Map
其中项目为:

类项目{
私有整数类型ID;
私有整数限制;
}
我找不到用snakeYaml实现这一点的方法,也找不到更好的库来完成这项任务

文档中只有嵌套在其他对象中的贴图/集合示例,因此您可以执行以下操作:

TypeDescription TypeDescription=新的TypeDescription(ClassContainingMap.class);
typeDescription.putMapPropertyType(“propertyNameOfNestedMap”,Integer.class,Item.class);
构造函数=新构造函数(类型描述);
Yaml Yaml=新的Yaml(建造商);
/*创建输入流(is)*/
ClassContainingMap obj=(ClassContainingMap)yaml.load(is);
但是,当地图格式位于文档的根目录下时,如何定义它呢?

您需要添加一个自定义的地图格式。但是,在您的情况下,您不希望注册“item”或“itemlist”标记

实际上,您希望应用于Yaml。它不是超高效的,但是有一种相对简单的方法可以做到这一点

class YamlConstructor extends Constructor {
  @Override
  protected Object constructObject(Node node) {

    if (node.getTag() == Tag.MAP) {
        LinkedHashMap<String, Object> map = (LinkedHashMap<String, Object>) super
                .constructObject(node);
        // If the map has the typeId and limit attributes
        // return a new Item object using the values from the map
        ...
    }
     // In all other cases, use the default constructObject.
    return super.constructObject(node);
类YamlConstructor扩展构造函数{
@凌驾
受保护对象constructObject(节点){
if(node.getTag()==Tag.MAP){
LinkedHashMap=(LinkedHashMap)超级
.constructObject(节点);
//如果映射具有typeId和limit属性
//使用映射中的值返回新的Item对象
...
}
//在所有其他情况下,使用默认的constructObject。
返回super.constructObject(节点);

以下是我在一个非常类似的情况下所做的。我只是在一个选项卡上标记了我的整个yml文件,并在顶部添加了一个map:tag。因此,对于您的情况,应该是这样

map:
  19:
    typeID: 2
    limit: 300
  20:
    typeID: 8
    limit: 100
然后在类中创建一个静态类,该类读取此文件,如下所示

static class Items {
    public Map<Integer, Item> map;
}
静态类项目{
公共地图;
}
然后阅读地图,只需使用

Yaml yaml = new Yaml(new Constructor(Items));
Items items = (Items) yaml.load(<file>);
Map<Integer, Item> itemMap = items.map;
Yaml-Yaml=新的Yaml(新的构造函数(项目));
Items=(Items)yaml.load();
Map itemMap=items.Map;
更新:

如果您不想或无法编辑您的yml文件,您可以在读取文件时使用类似的代码执行上述转换

try (BufferedReader br = new BufferedReader(new FileReader(new File("example.yml")))) {
    StringBuilder builder = new StringBuilder("map:\n");
    String line;
    while ((line = br.readLine()) != null) {
        builder.append("    ").append(line).append("\n");
    }

    Yaml yaml = new Yaml(new Constructor(Items));
    Items items = (Items) yaml.load(builder.toString());
    Map<Integer, Item> itemMap = items.map;
}
try(BufferedReader br=new BufferedReader(new FileReader(新文件(“example.yml”))){
StringBuilder=新的StringBuilder(“映射:\n”);
弦线;
而((line=br.readLine())!=null){
builder.append(“”).append(行).append(“\n”);
}
Yaml Yaml=新的Yaml(新的建造商(项目));
Items=(Items)yaml.load(builder.toString());
Map itemMap=items.Map;
}

要保持类型安全,您需要控制根节点的构造。为此,您可以使用构造函数的rootTag属性来标记根节点、修复子节点的类型并构造映射

自定义构造函数应该如下所示

public static class MyConstructor extends Constructor {
    private TypeDescription itemType = new TypeDescription(Item.class);

    public MyConstructor() {
        this.rootTag = new Tag("myRoot");
        this.addTypeDescription(itemType);
    }

    @Override
    protected Object constructObject(Node node) {
        if ("myRoot".equals(node.getTag().getValue()) && node instanceof MappingNode) {
            MappingNode mNode = (MappingNode) node;
            return mNode.getValue().stream().collect(
                    Collectors.toMap(
                            t -> super.constructObject(t.getKeyNode()),
                            t -> {
                                Node child = t.getValueNode();
                                child.setType(itemType.getType());
                                return super.constructObject(child);
                            }
                    )
            );

        } else {
            return super.constructObject(node);
        }
    }
}
下面是一个功能完整的示例

import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.Tag;

import java.util.Map;
import java.util.stream.Collectors;

public class Foo {

public static void main(String[] args) {
    String theYaml = "19:\n" +
            "  typeID: 2\n" +
            "  limit: 300\n" +
            "20:\n" +
            "  typeID: 8\n" +
            "  limit: 100";

    Yaml yaml = new Yaml(new MyConstructor());
    Map<String, Item> data = yaml.load(theYaml);
    System.out.println(data);
}

public static class Item {
    private int typeID, limit;

    public int getTypeID() {
        return typeID;
    }

    public void setTypeID(int typeID) {
        this.typeID = typeID;
    }

    public int getLimit() {
        return limit;
    }

    public void setLimit(int limit) {
        this.limit = limit;
    }
}

public static class MyConstructor extends Constructor {
    private TypeDescription itemType = new TypeDescription(Item.class);

    public MyConstructor() {
        this.rootTag = new Tag("myRoot");
        this.addTypeDescription(itemType);
    }

    @Override
    protected Object constructObject(Node node) {
        if ("myRoot".equals(node.getTag().getValue()) && node instanceof MappingNode) {
            MappingNode mNode = (MappingNode) node;
            return mNode.getValue().stream().collect(
                    Collectors.toMap(
                            t -> super.constructObject(t.getKeyNode()),
                            t -> {
                                Node child = t.getValueNode();
                                child.setType(itemType.getType());
                                return super.constructObject(child);
                            }
                    )
            );

        } else {
            return super.constructObject(node);
        }
    }
}
}

哇,Java中的YAML支持正在快速增长。虽然这个解决方案有效,但在处理嵌套结构时会让人头疼。我想我会将所有内容转换为JSON并使用Jackson进行解析。是的。Java(/scala)中的YAML解析非常糟糕。来自python,它是“免费的”@javadba绝对是。我来自python,有一半的时间我发现自己在与语言作斗争以避开自己的路。我感到非常拘束,完全没有任何自由。肯定有比这更好的方法了!(我会给出更好的答案……如果我知道怎么做的话:-)如果您可以修改或创建自己的文件,那么这实际上是非常聪明的。请参阅上面的更新,了解如何在不修改底层文件的情况下使用相同的技巧。这几乎是一种黑客行为。但它胜过了我所看到的用java解析yaml的任何其他方法(实际上在我的例子中是scala!).Upvoted。我想你肯定可以这样做。但是映射中的对象不会被构造成Item类型的对象。因此,你只能一路处理映射,而不是使用正确的访问器方法正确地处理“Item”类型的“类型安全”对象。所以我不认为这是问题的答案。@Kris你完全正确,我没听清楚OP试图保留孩子的类型。我已经更新了答案以反映正确的解决方案。
Map<String, Object> data = new Yaml().load(yamldata);
val data: java.util.Map[String, Any] = new Yaml().load(yamlData)