使用XElements.Decendents()以程序化方式构建XML文档
本文关键字:方式 构建 XML 文档 程序化 XElements Decendents 使用 | 更新日期: 2023-09-27 17:57:56
我正在编写一个应用程序,该应用程序与side-word一起工作,并为其创建自定义菜单系统。
我希望用户能够本质上添加无限的子菜单。
我正在从一个文本文件中读取,其中的选项卡表示缩进。
示例文本文件:
GROUP
MENU1
SUBMENU11
SUBSUBMENU111
SUBSUBMENU1111
SUBMENU12
MENU2
SUBMENU21
SUBSUBMENU211
XML输出示例:
<group id="GROUP" label="GROUP">
<menu id="MENU1" label="MENU1" size="normal">
<menu id="SUBMENU11" label="SUBMENU11" size="normal" >
<menu id="SUBMENU111" label="SUBMENU111" size="normal" >
<menu id="SUBMENU1111" label="SUBMENU1111" size="normal" />
</menu>
</menu>
<menu id="SUBMENU12" label="SUBMENU12" size="normal" />
</menu>
<menu id="MENU2" label="MENU2" size="normal" >
<menu id="SUBMENU21" label="SUBMENU21" size="normal" >
<menu id="SUBMENU211" label="SUBMENU211" size="normal" />
</menu>
</menu>
</group>
目前要在某些点添加,即在5个选项卡下:
path.Descendants(ns + "menu").ElementAt(currentMenuIndex)
.Descendants(ns + "menu").ElementAt(currentMenu1Index)
.Descendants(ns + "menu").ElementAt(currentMenu2Index)
.Descendants(ns + "menu").LastOrDefault()
.Add(//add node info);
我目前正在为每个选项卡案例创建这些子代树中的每一个,这使得代码变得庞大,如果可以的话,我希望能够为无限多的选项卡以编程方式完成这项工作。
你能透露的任何信息都将是美妙的,我已经绕了好几天了。
在Xml文档中创建XElement
和XAttribute
有一个简单得多的语法
new XElement("group",
new XAttribute("id", "GROUP"),
new XElement("menu",
new XAttribute("id", "MENU1"),
etc
编辑这里的递归策略如何,使用一些Linq:
const string menu = // I've hardcoded tabs and newlines as SO formats these out.
"GROUP'r'n" +
"MENU1'r'n" +
"'tSUBMENU11'r'n" +
"'t'tSUBSUBMENU111'r'n"+
"'t't'tSUBSUBMENU1111'r'n" +
"'tSUBMENU12'r'n"+
"MENU2'r'n" +
"'tSUBMENU21'r'n"+
"'t'tSUBSUBMENU211";
var sr = new StringReader(menu); // Obviously use a TextReader
var lines = sr.ReadToEnd()
.Split(new[] { Environment.NewLine },
StringSplitOptions.RemoveEmptyEntries);
var indentedLines =
lines
.Skip(1) // Group isn't part of the menu
.Select(
_ => new Tuple<string, int>( // Remove whitespace
Regex.Replace(_, @"'s+", ""),
_.Split(new[] { ''t' }).Count())) // Count the tabs
.ToList();
var doc = new XDocument();
doc.Add(new XElement("group", // Add the root group
new XAttribute("id", lines[0].Trim()),
new XAttribute("label", lines[0].Trim()),
BuildTree(indentedLines, 1, String.Empty))); // Add the top lvl nodes
有了这个递归助手:
IEnumerable<XElement> BuildTree(
IList<Tuple<string, int>> indentedLines,
int indentLevel, String parentNode)
{
Func<string, string> getNodeParent =
node =>
{
var line = indentedLines.First(_ => _.Item1 == node);
for (var index = indentedLines.IndexOf(line); index >= 0; index --)
{
if (indentedLines[index].Item2 < line.Item2)
{
return indentedLines[index].Item1;
}
}
return String.Empty; // has no parent
};
foreach (var line in indentedLines.Where(_ => _.Item2 == indentLevel
&& getNodeParent(_.Item1) == parentNode))
{
yield return new XElement("menu",
new XAttribute("id", line.Item1),
new XAttribute("label", line.Item1),
new XAttribute("size", "normal"),
BuildTree(indentedLines, indentLevel + 1, line.Item1));
}
}
生成这个Xml,我认为这就是你想要的:
<group id="GROUP" label="GROUP">
<menu id="MENU1" label="MENU1" size="normal">
<menu id="SUBMENU11" label="SUBMENU11" size="normal">
<menu id="SUBSUBMENU111" label="SUBSUBMENU111" size="normal">
<menu id="SUBSUBMENU1111" label="SUBSUBMENU1111" size="normal" />
</menu>
</menu>
<menu id="SUBMENU12" label="SUBMENU12" size="normal" />
</menu>
<menu id="MENU2" label="MENU2" size="normal">
<menu id="SUBMENU21" label="SUBMENU21" size="normal">
<menu id="SUBSUBMENU211" label="SUBSUBMENU211" size="normal" />
</menu>
</menu>
</group>
如果我们可以将当前菜单索引存储在一个数组中:
object[] currentMenuIndices;
void addNodeAtNTabs(nodeInfo, currentMenuIndices) {
currentPath = path;
name = ns + "menu"
foreach menuIndex in currentMenuIndices {
currentPath = Descendants(name).ElementAt(menuIndex);
}
currentPath.Descendants(name).LastOrDefault().Add(nodeInfo);
}
currentMenuIndices的长度应相对于节点缩进的选项卡数。(很抱歉,如果我的代码看起来像python)