自定义Yaml标签解析

news/2025/1/14 7:18:03/

该例子是模仿 sharding-jdbc 中的自定义标签对 List 中不同的对象进行解析,例如:这个yml配置文件 test 集合按照不同的 tag 进行区分,我们需要将不同的 tag 解析为不同的类

test:- !TYPE1name: zhj- !TYPE2age: 18

这里我们使用了 snakeyaml 库的依赖,这也是 springboot 的默认依赖库

<dependency><groupId>org.yaml</groupId><artifactId>snakeyaml</artifactId><version>1.33</version>
</dependency>

自定义构造器,添加对应的 TypeDescription 类型描述器,用于将对应的标签和类进行关联起来

/*** 自定义构造器*/
public class CustomConstruct extends Constructor {public CustomConstruct(LoaderOptions options) {super(options);this.addTypeDescription(new TypeDescription(CustomType1.class, "!TYPE1"));this.addTypeDescription(new TypeDescription(CustomType1.class, "!TYPE2"));}public CustomConstruct() {this(new LoaderOptions());}
}

自定义结构体

@Data
public class CustomType1 {private String name;
}
@Data
public class CustomType2 {private Integer age;
}

测试代码

public class YmlParser {public static void main(String[] args) {Constructor construct = new CustomConstruct();Yaml yaml = new Yaml(construct);//读取classpath下面的application.yml文件Object obj = yaml.load(YmlParser.class.getClassLoader().getResourceAsStream("application.yml"));System.out.println(obj);}
}

原理解析:
我们自定义的 CustomConstructor 继承至 Constructor 所以会默认注册下面的构造器,由于 snakeyaml 库会将每一个节点 Node 解析为对应 Tag 类型的数据,数据下面都可以找到对应的构造器

public Constructor(TypeDescription theRoot, Collection<TypeDescription> moreTDs, LoaderOptions loadingConfig) {//先调用父类注册对应node节点类型构造器,父类中也默认注册了许多 Tag 类型的构造器super(loadingConfig);if (theRoot == null) {throw new NullPointerException("Root type must be provided.");}//如果节点的类型为null,默认使用 ConstructYamlObject,这个也是我们自定义标签处理的类this.yamlConstructors.put(null, new ConstructYamlObject());if (!Object.class.equals(theRoot.getType())) {rootTag = new Tag(theRoot.getType());}yamlClassConstructors.put(NodeId.scalar, new ConstructScalar());yamlClassConstructors.put(NodeId.mapping, new ConstructMapping());yamlClassConstructors.put(NodeId.sequence, new ConstructSequence());addTypeDescription(theRoot);if (moreTDs != null) {for (TypeDescription td : moreTDs) {addTypeDescription(td);}}}

这里我们可以通过 yaml.load() 方法定位到调用的是 constructor 类,也是我们上面自定义好的构造器类

private Object loadFromReader(StreamReader sreader, Class<?> type) {//将对应的数据流按照类型进行解析为 Node 节点Composer composer = new Composer(new ParserImpl(sreader, this.loadingConfig), this.resolver, this.loadingConfig);this.constructor.setComposer(composer);//根据需要class类型进行解析,默认根节点的类型是objectreturn this.constructor.getSingleData(type);}protected Object constructObjectNoCheck(Node node) {//判断当前节点有没有解析过if (recursiveObjects.contains(node)) {throw new ConstructorException(null, null, "found unconstructable recursive node",node.getStartMark());}recursiveObjects.add(node);//根据node类型获取到对应的Construct,通过 yamlConstructors中获取对应的构造器Construct constructor = getConstructor(node);//判断是否已经解析了,如果没有解析直接调用构造器进行解析,通过debug我们可以知道自定义的tag解析时是使用 ConstructYamlObjectObject data = (constructedObjects.containsKey(node)) ? constructedObjects.get(node): constructor.construct(node);finalizeConstruction(node, data);//解析完成之后存入缓存中constructedObjects.put(node, data);//移出当前节点是否正在解析recursiveObjects.remove(node);if (node.isTwoStepsConstruction()) {constructor.construct2ndStep(node, data);}return data;}

通过debug到 ConstructYamlObject 其中内部代码又是通过 ConstructMapping 进行处理的,解析自定义的List中的元素呢,解析出的节点类型是 Object 对象类型如果走的是else分支,通过 constructJavaBean2ndStep 源码可以看到获取到对应节点的类型并且到 typeDefinitions 缓存中获取到具体的关联解析类,然后就可以根据对应的tag进行解析了

public Object construct(Node node) {MappingNode mnode = (MappingNode)node;//判断节点class类型是否是mapif (Map.class.isAssignableFrom(node.getType())) {return node.isTwoStepsConstruction() ? Constructor.this.newMap(mnode) : Constructor.this.constructMapping(mnode);//判断节点类型是否是Collection} else if (Collection.class.isAssignableFrom(node.getType())) {return node.isTwoStepsConstruction() ? Constructor.this.newSet(mnode) : Constructor.this.constructSet(mnode);} else {//判断节点类型是否是对象Object obj = Constructor.this.newInstance(mnode);if (obj != BaseConstructor.NOT_INSTANTIATED_OBJECT) {return node.isTwoStepsConstruction() ? obj : this.constructJavaBean2ndStep(mnode, obj);} else {}}}protected Object constructJavaBean2ndStep(MappingNode node, Object object) {...省略代码//获取到对应的keyString key = (String)Constructor.this.constructObject(tuple.getKeyNode());try {//通过对应的beanType类型获取到对应的 TypeDescription 关联对象TypeDescription memberDescription = (TypeDescription)Constructor.this.typeDefinitions.get(beanType);Property property = memberDescription == null ? this.getProperty(beanType, key) : memberDescription.getProperty(key);if (!property.isWritable()) {throw new YAMLException("No writable property '" + key + "' on class: " + beanType.getName());}}
}

上面的源码解析呢只是通过debug进行大致的跟踪,最后到具体的处理类,主要是了解了一下 Constructor和TypeDescription 的关系,了解到 Constructor 主要是用于操控整个解析和实例化的流程,而 TypeDescription 则是定义具体的标签和 class类之间的关系。


http://www.ppmy.cn/news/600038.html

相关文章

css——背景图片 保持比例不变形

代码 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title><style>html{height: 100%;}body{/* background: url(img/2.jpg) no-repeat top center;background-size: container; */background: url(img/…

Python爬虫入门系列之多线程爬虫优化

Python爬虫入门系列之多线程爬虫优化 随着互联网数据的急剧增加和页面结构的复杂化&#xff0c;使用单线程爬虫可能会面临性能瓶颈和效率低下的问题。为了充分利用计算机的多核处理能力&#xff0c;我们需要考虑使用多线程来优化爬虫程序。 多线程爬虫优化的关键点 以下是一些…

Kafka消息队列面临的优化问题

文章目录 1.如何防止消息被重复消费2.如何保证消息的顺序消费3.如何解决消息积压的问题4.延迟队列的实现方式 1.如何防止消息被重复消费 在防止消息丢失的方案中&#xff0c;如果生产者发送完消息后&#xff0c;因为网络的波动&#xff0c;没有收到Kafka返回的ACK确认收到信息…

计算机端口丢失,win7设备管理器“里头没有了”端口“选项

请不要盗用我的答案&#xff01;&#xff01; 一号方案【新P】 注意【原创】&#xff1a; 1&#xff0e;安全模式下&#xff0c;效果更好&#xff01; 2. 以下所要使用的软件&#xff0c;都要安装或升级到最新版本&#xff0c;以保证使用的效果。 3. 不杀毒&#xff0c;直接使用…

计算机丢失xmlLite.dll,电脑总是提醒应用程序错误或DLL C:windowssystem32xmllite.dll为无效的windows映像。。。这是为什么...

请不要盗用我的答案&#xff01;&#xff01; 一号方案【新P】 注意【原创】&#xff1a; 1&#xff0e;安全模式下&#xff0c;效果更好&#xff01; 2. 以下所要使用的软件&#xff0c;都要安装或升级到最新版本&#xff0c;以保证使用的效果。 3. 不杀毒&#xff0c;直接使用…

计算机脚本发生错误,我的电脑开机后显示当前页面的脚本发生错误?

1.右击IE图标——属性——高级——选中“禁用脚本调试”&#xff0c;取消“显示每个脚本错误的通知”&#xff1b; 2.右击我的电脑图标——属性——高级——错误报告——选中“禁用错误报告”。 还不行&#xff0c;就------------ 请不要盗用我的答案&#xff01;&#xff01; …

计算机桌面图标有背影,电脑界面快捷方式后面的蓝背影怎么去掉

【我的答案使用已久了】 右键点“我的电脑”&#xff0c;左键点“属性”、“高级”、“设置”、再点“性能”里的“设置”&#xff0c;在“视觉效果”选项卡中选择“自定义”&#xff0c;然后在“在桌面上为图标标签使用阴影”这一条前面保持打勾&#xff0c;然后确定即可。 适…

台式电脑一直跳一个计算机页面出来,我的电脑界面上莫名其妙的多出了好几个IE浏览器拜托各位了 3Q...

请不要盗用我的答案&#xff01;&#xff01; 注意【原创】&#xff1a; 【用于删除桌面上难以删去的图标和文件夹】 1&#xff0e;安全模式下&#xff0c;效果更好&#xff01; 2.以下所要使用的软件&#xff0c;都要安装或升级到最新版本&#xff0c;以保证使用的效果。 3. 不…