重建XML结构成一个递归样式列表/ XMLReader的替代
问题描述:
问题:重建XML结构成一个递归样式列表/ XMLReader的替代
我一直在试图解析XML,并在XML的每个节点创建一个对象。
问题:
由于我的XML已经以任意顺序的节点,有些是其他节点的子节点,我挣扎着逻辑上分析它们不使用.NET 1.1和XmlNode的类。
注意:我希望只使用XMLReader,因为我仅限于.Net Standard 1.0,并且不想安装任何额外的库。
目前,我为每个xml节点创建一个对象,每个对象都包含一个我希望添加到的子组件列表,如果它找到了一个子节点。不过,我似乎无法递归搜索xml,并正确地生成列表/列表的结构。
我的代码:
using System.IO;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApp1
{
class Program
{
static List<UIComponent> components = new List<UIComponent>();
static void Main(string[] args)
{
String path = "C:\\Users\\admin\\Desktop\\test.xml";
parseXML(path);
}
private static UIComponent addToLowestChild(UIComponent parent, UIComponent child)
{
for(int i=0;i<parent.getChildren().Count;++i)
{
if (parent.getChildren().Count > 0)
{
foreach (UIComponent kid1 in parent.getChildren())
{
if (kid1.getChildren().Count > 0)
{
addToLowestChild(kid1, child);
}
}
}
}
return parent;
}
private static UIComponent ChildTest(int depth,UIComponent parent,UIComponent child)
{
//i=depth for item 4, if i is set to 1 first
for (int i = 1; i < depth; ++i)
{
if (parent.getChildren().Count > 0)
{
if (i > 1)
{
ChildTest(i, parent.getChildren()[parent.getChildren().Count - 1], child);
}
else
{
parent.getChildren()[parent.getChildren().Count - 1].addChild(child);
break;
}
}
else
{
parent.addChild(child);
break;
}
}
return parent;
}
private static void parseXML(string path)
{
//read the xml file into one string
string[] lines = System.IO.File.ReadAllLines(path);
string xmlContent = "";
foreach (string line in lines)
{
xmlContent += line;
}
UIComponent currentComponent = null;
//parse the xml content
using (XmlReader reader = XmlReader.Create(new StringReader(xmlContent)))
{
while (reader.Read())
{
int currentDepth = 0;
// Console.WriteLine(reader.Name+" depth:"+reader.Depth);
if (reader.Depth > 0) //ignore ground level elements such as <XML> and <UI>
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
if (currentComponent == null || reader.Depth==currentDepth)
{
currentComponent = new UIComponent(reader.Name);
}
else
{
UIComponent childComponent = new UIComponent(reader.Name);
//currentComponent.addChild(childComponent);
ChildTest(reader.Depth,currentComponent,childComponent);
}
break;
case XmlNodeType.Text:
break;
case XmlNodeType.XmlDeclaration:
case XmlNodeType.ProcessingInstruction:
break;
case XmlNodeType.Comment:
break;
case XmlNodeType.EndElement:
if (reader.Depth == 1 && currentComponent!=null)
{
components.Add(currentComponent);
currentComponent = null;
}
break;
default: break;
}
}
}
}
}
我的测试数据
<?xml version="1.0" encoding="UTF-8"?>
<UI>
<window x="5">
<Button2 value="">
<Button3 y="">
</Button3>
<Button4>
<Button6 value=""></Button6>
<Button5 value=""></Button5>
</Button4>
</Button2>
</window>
<window></window>
<heading></heading>
</UI>
输出:
-Window
|-Button 2
|-- Button 3
|-- Button 4
|-- Button 6
|-- Button 5
-Window
-Heading
我想要什么:
-Window
|-Button 2
|-- Button 3
|-- Button 4
|-- Button 6
|-- Button 5
-Window
-Heading
答
您只需要维护当前处理节点的堆栈。每次你点击StartElement
时,你都会创建一个新节点,读取属性,将它添加到父节点或根节点列表中,如果它不是空元素(如<window />
),则将它推入堆栈。当你点击EndElement
时,你只需弹出(移除)堆栈的最后一个元素。栈顶元素表示当前正在处理的节点。
付诸行动,解析XML像这样简单的类的列表:
class Node
{
public string Name;
public List<Node> Children = new List<Node>();
public Dictionary<string, string> Attributes = new Dictionary<string, string>();
public override string ToString() => Name;
}
可能是这样的:
static List<Node> ParseXML(string xmlContent)
{
using (var reader = XmlReader.Create(new StringReader(xmlContent)))
{
var rootNodes = new List<Node>();
var nodeStack = new Stack<Node>();
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
var node = new Node { Name = reader.Name };
if (reader.MoveToFirstAttribute())
{
// Read the attributes
do
{
node.Attributes.Add(reader.Name, reader.Value);
}
while (reader.MoveToNextAttribute());
// Move back to element
reader.MoveToElement();
}
var nodes = nodeStack.Count > 0 ? nodeStack.Peek().Children : rootNodes;
nodes.Add(node);
if (!reader.IsEmptyElement)
nodeStack.Push(node);
break;
case XmlNodeType.EndElement:
nodeStack.Pop();
break;
}
}
return rootNodes;
}
这里从XmlReader中的描述是:“*代表读者它提供对XML数据的快速,非缓存,只前向访问*“。什么让你认为你实际上可以添加到它? –
什么是* .net 1.0 *? –
我更新了这个问题,以便为将来的读者解决这个问题。谢谢你的帮助@IvanStoev! – D3181