问题:
我一直在尝试解析xml,并为XML中的每个节点创建一个对象。
问题:
由于我的xml有任何顺序的节点,有些是其他节点的子节点,我在努力逻辑解析它们而不使用.net 1.1和XmlNode类。
注意:我希望只使用XMLReader,因为我被限制为.Net Standard 1.0,并且不想安装任何额外的库。 (见这里:https://docs.microsoft.com/en-us/dotnet/standard/net-standard)
目前,我为每个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
答案 0 :(得分:3)
您只需要维护当前处理节点的堆栈。无论何时点击StartElement
,您都会创建一个新节点,读取属性,将其添加到父节点或根节点列表,如果它不是空元素(如<window />
),把它推到堆栈。当你点击EndElement
时,你只需弹出(删除)堆栈的最后一个元素。 stack top元素表示当前正在处理的节点。
将其付诸实践,将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;
}