如何使用 <Data>snakeyaml 从 yaml 获取 Map<String,List>



尝试从yaml加载数据并创建以下对象:

问题:无法将yaml映射到对象,当我尝试时,它总是抛出异常:java.util.LinkedHashMap无法转换为com.heraizen.DataConfig$Data

Map<String,List<Data>> map;

代码片段:

public class DataConfig {
private Map<String, List<Data>> heros = new HashMap<String, List<Data>>();
public static class Data {
private String name;
private String uri;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
@Override
public String toString() {
return "Data [name=" + name + ", uri=" + uri + "]";
}
}
public Map<String, List<Data>> getHeros() {
return heros;
}
public void setHeros(Map<String, List<Data>> heros) {
this.heros = heros;
}

亚姆尔:

--- 
heros: 
one:
- name: h1
url: hu2
- name: h2
url: hu22
two: 
- name: h3
url: hu3

构造对象:

Yaml yaml = new Yaml(new Constructor(DataConfig.class));
DataConfig retValue = (DataConfig) yaml.load(new FileInputStream("one.yaml"));
retValue.getHeros().entrySet().forEach(ele->{
System.out.println(ele.getKey()+" "+ele.getValue().get(0).getName());
});
System.out.println(retValue);

可以使用自定义构造函数

public class MyDataConstructor extends Constructor {
private static final String MY_DATA_CONSTRUCTOR_NAME = MyDataConstructor.class.getSimpleName();
public MyDataConstructor(LoaderOptions loadingConfig) {
super(loadingConfig);
this.rootTag = new Tag(MY_DATA_CONSTRUCTOR_NAME);
}
@Override
protected Object constructObject(Node node) {
if (MY_DATA_CONSTRUCTOR_NAME.equals(node.getTag().getValue()) && node instanceof MappingNode mNode) {
return mNode.getValue().stream().collect(
Collectors.toMap(
t -> super.constructObject(t.getKeyNode()),
t -> {
Node child = t.getValueNode();
if (child instanceof SequenceNode sNode) {
sNode.setListType(Data.class);
}
return super.constructObject(child);
}
)
);
}
return super.constructObject(node);
}
@lombok.Data
public static class Data {
private String name;
private String uri;
}
public static void main(String[] args) {
try {
Yaml yl = new Yaml(new MyDataConstructor(new LoaderOptions()));
String ylStr =
"custom-xxx:n" +
"  - name: qwen" +
"    uri: wwwwn" +
"  - name: asdn" +
"    uri: xxxxn" +
"  - name: zxcn" +
"    uri: rrr";
Map<String, List<Data>> my = yl.load(ylStr);
for (String key : my.keySet()){
for(Data item: my.get(key)){
System.out.println(item.getName());
System.out.println(item.getUri());
System.out.println(item.getClass().getName());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

输出:

> Task :MyDataConstructor.main()
qwe
wwww
xxx.MyDataConstructor$Data
asd
xxxx
xxx.MyDataConstructor$Data
zxc
rrr
xxx.MyDataConstructor$Data

附言:

dependencies {
implementation "org.yaml:snakeyaml:2.0"
}

引用构造函数

由于DataConfig不是LinkedHashMap的子类型或超类型,因此不能在那里使用强制转换。我不熟悉 yaml,但我可以假设该方法yaml.load()从您的异常描述中返回LinkedHashMap

尝试编写一个属于DataConfig的静态方法,该方法将LinkedHashMap转换为DataConfig

public static DataConfig parseNew(LinkedHashMap map) {
}

若要解决此问题,应将 TypeDescription 添加到Yaml对象,并手动映射基础属性列表的类型:

yaml.addTypeDescription(new TypeDescription(DataConfig.class) {
@Override
public boolean setupPropertyType(String key, Node valueNode) {
if ("heros".equals(key) &&  valueNode instanceof MappingNode) {
MappingNode mappingNode = (MappingNode) valueNode;
boolean listTypeDefined = false;
for (NodeTuple nodeTuple : mappingNode.getValue()) {
if (nodeTuple.getValueNode() instanceof SequenceNode) {
SequenceNode sequenceNode = (SequenceNode) nodeTuple.getValueNode();
sequenceNode.setListType(Data.class);
listTypeDefined = true;
}
}
return listTypeDefined;
}
return super.setupPropertyType(key, valueNode);
}
});

如果执行此操作并运行程序,则应看到以下输出:

one h1
two h3
Test$DataConfig@6a79c292

请注意示例中的一个小错误,您的数据类有一个属性url,而在 yaml 中它是uri

最新更新