java 使用snakeYaml在根处解析带有地图的YAML文档
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28551081/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Parsing a YAML document with a map at the root using snakeYaml
提问by Justas
I want to read a YAML document to a map of custom objects (instead of maps, which snakeYaml does by default). So this:
我想将 YAML 文档读取到自定义对象的地图(而不是蛇形Yaml 默认情况下所做的地图)。所以这:
19:
typeID: 2
limit: 300
20:
typeID: 8
limit: 100
Would be loaded to a map which looks like this:
将加载到如下所示的地图中:
Map<Integer, Item>
where Item is:
其中项目是:
class Item {
private Integer typeId;
private Integer limit;
}
I could not find a way to do this with snakeYaml, and I couldn't find a better library for the task either.
我找不到用snakeYaml 做到这一点的方法,我也找不到更好的库来完成这项任务。
The documentation only has examples with maps/collections nested inside other objects, so that you can do the following:
该文档仅包含嵌套在其他对象中的映射/集合的示例,因此您可以执行以下操作:
TypeDescription typeDescription = new TypeDescription(ClassContainingAMap.class);
typeDescription.putMapPropertyType("propertyNameOfNestedMap", Integer.class, Item.class);
Constructor constructor = new Constructor(typeDescription);
Yaml yaml = new Yaml(constructor);
/* creating an input stream (is) */
ClassContainingAMap obj = (ClassContainingAMap) yaml.load(is);
But how do I go about defining the Map format when it is at the root of the document?
但是,当 Map 格式位于文档的根部时,我该如何定义它呢?
采纳答案by Emily Crutcher
You need to add a custom Constructor. However, in your case you don't want register an "item" or "item-list" tag.
您需要添加一个自定义的Constructor。但是,在您的情况下,您不想注册“项目”或“项目列表”标签。
In effect, you want to apply Duck Typingto your Yaml. It's not super efficient, but there is a relatively easy way to do this.
实际上,您希望将Duck Typing应用到您的 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);
回答by crowmagnumb
Here is what I did for a very similar situation. I just tabbed my whole yml file over one tab and added a map: tag to the top. So for your case it would be.
这是我为非常相似的情况所做的。我只是将我的整个 yml 文件放在一个标签上,并在顶部添加了一个 map: 标签。所以对于你的情况,它会是。
map:
19:
typeID: 2
limit: 300
20:
typeID: 8
limit: 100
And then create a static class in your class that reads this file like follows.
然后在你的类中创建一个静态类来读取这个文件,如下所示。
static class Items {
public Map<Integer, Item> map;
}
And then to read your map just use.
然后阅读您的地图只需使用。
Yaml yaml = new Yaml(new Constructor(Items));
Items items = (Items) yaml.load(<file>);
Map<Integer, Item> itemMap = items.map;
UPDATE:
更新:
If you don't want to or cannot edit your yml file you could just do the above transform in code while reading the file with something like this.
如果您不想或不能编辑您的 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;
}
回答by Narcoleptic Snowman
To keep type safety, you need to take control of the root node construction. To do this, you can use the rootTag property of the Constructor to mark the root node, fix the type of the children, and construct the map.
为了保持类型安全,您需要控制根节点的构造。为此,您可以使用构造函数的 rootTag 属性来标记根节点、固定子节点的类型并构建映射。
The custom constructor should look like the following
自定义构造函数应如下所示
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);
}
}
}
Here's fully functional example
这是功能齐全的示例
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);
}
}
}
}
If you don't care about the types, and just want duck typing (everything is a map), You can load with no settings.
如果您不关心类型,而只想打字(一切都是地图),则无需设置即可加载。
Java
爪哇
Map<String, Object> data = new Yaml().load(yamldata);
Scala
斯卡拉
val data: java.util.Map[String, Any] = new Yaml().load(yamlData)