将多个路径字符串转换成XML文档树

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介:

假设有下面的字符串:

?
1
2
3
4
5
6
7
/home/usr/abc/def/文本.txt
/home/usr/desktop/音乐.mp3
/etc/init.d/mysql/mysql
/etc/profile
/tmp/垃圾.tmp
/usr/bin/open-jdk7/java
...

给定一个根节点名字root和叶子节点名字leaf,如何将它们转换成一颗像下面这样的XML文档树呢?

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
< root >
   < home >
     < usr >
       < abc >
         < leaf >文本.txt</ leaf >
       </ abc >
       < desktop >
         < leaf >音乐.mp3</ leaf >
       </ desktop >
     </ usr >
   </ home >
   < etc >
     < init.d >
       < mysql >
         < leaf >mysql</ leaf >
       </ mysql >
     </ init.d >
     < leaf >profile</ leaf >
   </ etc >
   < tmp >
     < leaf >垃圾.tmp</ leaf >
   </ tmp >
   < usr >
     < bin >
       < open-jdk7 >
         < leaf >java</ leaf >
       </ open-jdk7 >
     </ bin >
   </ usr >
</ root >

对于这个问题,一个解决的思路是:先创建一颗以root作为根标签的XML文档树,再循环迭代每个字符串,将其按照'/'切开(Split),然后依次对每个文件夹名字创建一个XML节点(Node),并搜索整棵树,如果该节点存在,则直接pass掉,否则将节该点追加到某个父节点下。但是此种方法太麻烦,因为每次增加一个节点,你就需要去遍历一次。有很多情况下,前几层节点是存在的,这样判断就不那么高效了。举个例子,现在有一条深度为10的文件夹路径(比如/a/b/c/d/e/f/g/h/i/j/**.sh),为了插入这条路径,首先需要判断/a是否存在,存在就pass掉,不存在就创建/a;之后判断/a/b是否存在;之后是/a/b/c是否存在。。。

很显然,这样做,越到后面效率越低。而且,直接操作Xml文档会占用很多资源。

那么,有没有一种简单又高效的方式呢?答案是肯定的,这就是写这篇博客的原因了。这只是一种方案,也许还有更好的,有兴趣的同学可以自行研究,哈哈……

首先,我们可以将这些文件夹路径进行预处理────转换成中间格式进行存储。这里,我们可以先定义一个Map结构,Key用于保存当前节点名字和节点的XPath,中间可用特殊字符隔开;Value用于保存当前节点的父节点的名字和父节点的XPath(根节点root的父亲为null),中间也用相同的特殊字符隔开,像下面这样:

?
1
2
< home #/root, root#null>
< user #/root/home, home#/root>

第一行表示:当前的home节点XPath为/root,其父节点root的xPath为null,同理,第二行表示:当前节点user的XPath为/root/home,而其父节点home的XPath为/root。把Key设计成这样有一个好处是,当一条路径中的多层里有相同名字的文件夹时,也可以轻易分辨。比如有一条路径为:/home/home/home,那么在创建节点时,会创建以下几个键值对:

?
1
2
3
<home#/root, root# null >
<home#/root/home, home#/root>
<home#/root/home/home, home#/root/home>

就是说,在同一个XPath(例如/root/home)下,只会存在一个名为home的文件夹。如果以后的文件夹路径中还包含/home/home/home这样的地址,那么这些文件夹的子文件夹将会被放在已经存在的/root/home/home/home路径下。再通俗一点:每个Key-Value都是唯一的,它唯一表示了一条路径的存在。

有人可能会问:为什么Value也要设计成一样的结构呢?

答案很简单,便于在以后的处理中直接使用这个值,后面会提到。

通过这样的转换,我们可以看出,第二行的value实际上就是第一行的key,这样我们就可以表示一条一条的子孙——祖宗链,都是由子节点指向父节点。

转换之后的Map像下面这样:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
< home #/root, root#null>
< user #/root/home, home#/root>
< abc #/root/home/user, user#/root/home>
< def #/root/home/user/abc, abc#/root/home/user>
<文本.txt#/root/home/user/abc/def, def#/root/home/user/abc/def>
 
<!-- 第二个URL中的home文件夹和user文件夹对应的key就是第一个key,
已经存在,则不会在增加这个key了 -->
< desktop #/root/home/user, user#/root/home>
<音乐.mp3#/root/home/user/desktop, desktop#/root/home/user>
 
< etc #/root, root#null>
< init.d #/root/etc, etc#/root>
< mysql #/root/etc/init.d, init.d#/root/etc>
< mysql #/root/etc/init.d/mysql, mysql#/rot/etc/init.d>
...

细心的同学可能已经看见了,第5行以后,处理第二个URL时,home和user两个文件夹已经存在,则直接pass掉,接着处理desktop文件夹了。还有desktop文件夹对应的value是user#/root/home,这和第3行的value相同,因此文件夹desktop和abc属于同一级,都在/root/home/user下。

转换后,我们可以做以下几件事:

  1. 直接遍历这个Map,先找出根目录(即value为root#null的)root下的所有节点(这里是home, etc, tmp, usr,当然也可能直接就是一个文件了)。判定的标准为:所有的value都为root#null的。

  2. 循环遍历root的子节点集合,判定每个子节点是否为叶子节点(即文件),若是,则加上<leaf>节点名字<leaf>,可以考虑使用StringBuilder.append();,若不是,则对每个子节点做以下几件事:

    1. 加上节点开始标记<节点名字>

    2. 做第1步,只是当前目录不是根目录而已(递归了)

    3. 加上节点结束标志<节点名字>


这样递归下去,就会形成多颗以文件夹路径开始的文件夹作为根目录的XML树,最后将所有得到的小树都添加到新创建的<root></root>根标签中,到此,文档生成完成。

代码就不写了,思路已经相当清晰了,哈哈

使用这种方式,虽然迭代次数可能比较多,但是使用Map来保存树的结构以及使用字符串来生成XML的资源消耗都不大,而且效率都相当高。至于有多高,我用以上数据做了个测试,执行时间在1~3ms,感兴趣的亲们可以试试。


后记

    开始的一个版本是:将Map中的Key和Value都表示为当前【节点名字#深度】,但是当我按照这种思路写完后,立刻发现生成的XML不是我想要的,因为我的测试数据中存在多个【节点名字】和【深度】都相同的节点,但是其根节点不同。。。。究其原因,是因为我们设计时可能存在同样的Key,于是后面的同深度且同名的文件夹名字则被pass掉了,导致该深度下所有同名的文件夹都被添加到了第一颗根节点下。

    后来想了半天,才把这个【节点名字#深度】替换为【节点名字#XPath】,这个必须是唯一的了。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
6月前
|
XML 存储 JavaScript
DOM 节点遍历:掌握遍历 XML文档结构和内容的技巧
**XML DOM 遍历、操作和导航概述** - 遍历XML节点树用于提取信息,例如,通过JavaScript的DOM API循环`&lt;book&gt;`子节点显示名称和值。 - DOM解析器处理XML文本数据,包括解析字符数据(PCDATA)和识别CDATA段。 - 节点导航涉及`parentNode`、`childNodes`等属性,`get_nextSibling`等辅助函数避免空文本节点。 - `getElementsByTagName`、`getAttribute`和`nodeValue`用于检索元素、属性值和文本。
89 6
DOM 节点遍历:掌握遍历 XML文档结构和内容的技巧
|
XML 数据格式
hutool将XML文档转换为String
hutool将XML文档转换为String
|
3月前
|
XML 数据采集 存储
使用Java和XPath在XML文档中精准定位数据
在数据驱动的时代,从复杂结构中精确提取信息至关重要。XML被广泛用于数据存储与传输,而XPath则能高效地在这些文档中导航和提取数据。本文深入探讨如何使用Java和XPath精准定位XML文档中的数据,并通过小红书的实际案例进行分析。首先介绍了XML及其挑战,接着阐述了XPath的优势。然后,提出从大型XML文档中自动提取特定产品信息的需求,并通过代理IP技术、设置Cookie和User-Agent以及多线程技术来解决实际网络环境下的数据抓取问题。最后,提供了一个Java示例代码,演示如何集成这些技术以高效地从XML源中抓取数据。
100 7
使用Java和XPath在XML文档中精准定位数据
|
5月前
|
XML 数据格式
XML配置Servlet文件,不使用注解配置路径的方法
XML配置Servlet文件,不使用注解配置路径的方法
|
6月前
|
XML 存储 C#
C# xml文档反序列化记事
本文介绍了使用XmlSerializer进行XML序列化和反序列化的关键点。包括:1) 以独占方式读取XML文件以避免并发问题;2) 当元素名与类型名不一致时,可通过`[XmlArrayItem]`指定元素名,或创建继承自原始类型的子类;3) 处理DateTime反序列化错误,通过中间字符串属性转换;4) 提到了常用C#特性如`[XmlRoot]`, `[XmlElement]`, `[XmlAttribute]`, `[XmlIgnore]`和`[XmlArrayItem]`的作用。
|
6月前
|
XML JavaScript 数据格式
XML Schema(XSD)详解:定义 XML 文档结构合法性的完整指南
XML Schema(XSD)是用于定义XML文档结构的规范,它描述了元素、属性及其顺序、数据类型和约束。与DTD相比,XML Schema支持更多数据类型,如字符串、日期等,并且是基于XML的,允许扩展和重用。学习XML Schema有助于确保数据通信时双方对内容的理解一致,通过验证防止错误。示例展示了如何定义一个`note`元素,包含`to`, `from`, `heading`和`body`子元素,都是字符串类型。XML文档可以通过引用XML Schema进行验证,确保内容符合预设模式。
461 0
|
6月前
|
XML JavaScript 前端开发
XML文档节点导航与选择指南
XPath是XSLT的核心部分,用于XML文档的节点定位和选择。它采用路径表达式语法,包含200多个内置函数处理各种数据类型。XPath在编程语言如JavaScript中广泛使用,与XSLT配合进行XML转换和样式处理。它涉及7种节点类型,如元素、属性和文本,以及多种节点间关系,如父、子、同级等。XPath还使用轴(如ancestor、child)来描述节点间的关联,并支持运算符进行逻辑和数学操作。
74 0
XML文档节点导航与选择指南
|
6月前
|
XML 存储 数据格式
探索 DTD 在 XML 中的作用及解析:深入理解文档类型定义
DTD(文档类型定义)用于定义XML文档的结构和合法元素、属性。它允许不同团体就数据交换标准达成一致,并用于验证XML数据的有效性。DTD可通过内部声明(在XML文档内)或外部声明(在单独文件中)来定义。内部声明示例显示了如何定义`note`元素及其子元素的结构,而外部声明则引用外部DTD文件。元素、属性、实体和PCDATA/CDATA是XML构建模块。DTD中,元素通过ELEMENT声明定义,属性通过ATTLIST声明定义,实体声明提供特殊字符或外部资源的快捷方式。
110 0
|
XML JavaScript 数据格式
使用Dom4J解析XML文档
XML解析的方式 XML常见的两种解析方式: DOM: 要求解析器将整个XML文件全部加载到内存中,生成一个Document对象 优点:元素和元素之间保留结构、关系,可以针对元素进行增删查改操作 缺点:如果XML文件过大,可能会导致内存溢出 SAX:是一种速度更快,更加高效的解析方式。它是逐行扫描,边扫描边解析,并且以事件驱动的方式来进行具体的解析,每解析一行都会触发一个事件 优点: 不会出现内存溢出的问题,可以处理大文件 缺点:只能读,不能写 概念辨析: 解析器就是根据不同的解析方式提供具体的实现。 为了方便开发人员来解析XML,有一些方便操作的类库。例如Dom4j其中就包含了很多解
107 0
|
XML Java 数据格式
解决SpringBoot获取mapper.xml路径的问题
当mapper.xml与mapper.class放在同一文件夹下时,是不能够将xml文件打包进项目的,项目构建的时候不会加载到target文件夹中。在pom.xml中加入如下这句:
354 0