【问题标题】:How to read/write nodes and child nodes from xml file in c# maybe using xpath?如何从 C# 中的 xml 文件读取/写入节点和子节点可能使用 xpath?
【发布时间】:2009-11-13 20:02:21
【问题描述】:

也许有人可以帮助我。我需要两种方法。

首先应该打开一个 xml 文件并使用给定参数获取所有节点,即:

XML 文件(file.xml):

<Menu id="1" Name="myMenu">
  <MenuItem Header="Header 1" Name="header1" />
  <MenuItem Header="Header 2" Name="header2">
    <MenuItem Header="subHeader 2.1" Name="header2_1">
      <MenuItem Header="subsubHeader 2.1.1" Name="header2_1_1" />
    </MenuItem>
  </MenuItem>
  <MenuItem Header="Header 3" Name="header3" />
</Menu>

所以,现在我需要使用这样的方法从 xml 中获取值:

public static List<string, string>ReadXML(string filename, string node, string[] attributes, bool searchSubNodes);

调用方法示例:ReadXMLValues("file.xml", "MenuItem", new string[] {"Header", "Name"}, true);

这将返回一个包含两个字符串的列表,例如:

"Header 1", "header1"
"Header 2", "header2"
"subHeader 2.1", "header2_1" <-- this should be in the list only if searchSubNodes is enabled!
"subsubHeader 2.1.1", "header2_1_1" <-- the same for this one!!!
"Header 3", "header3"

这是阅读部分,现在是写作部分:

文件名与上面的file.xml 相同。 public static void WriteXML(string filename, string node, List attributes);

现在让我们说 file.xml 有这样的空头属性:

<Menu id="1" Name="myMenu">
  <MenuItem Header="" Name="header1" />
  <MenuItem Header="" Name="header2">

我需要将值放入标题中,最终结果应如下所示:

   <Menu id="1" Name="myMenu">
      <MenuItem Header="Header 1" Name="header1" />
      <MenuItem Header="Header 2" Name="header2">

这样的事情可能吗??? C# 大师和其他知道如何做到这一点的人请帮助我!我不知道该怎么做。

最好的问候!

【问题讨论】:

  • 我正在做一个项目并且不掌握 XPath。
  • 编辑了我的答案以展示如何使用 XPath(在 C# 中)选择所需的节点。

标签: c# xml xpath


【解决方案1】:

您可以使用 XPath 进行所有搜索。

XmlNodeList nodes = doc.SelectNodes("/Menu/MenuItem");

在这种情况下,nodes 将包含所有 MenuItem 元素,它们是顶级 Menu 元素的子元素。

XmlNodeList nodes = doc.SelectNodes("/Menu/MenuItem | /Menu/MenuItem/MenuItem");

在这种情况下,nodes 将包含所有 MenuItem 元素,这些元素是 MenuMenuItem 的子元素,而 MenuItemMenu 的子元素。

您还可以找到所有 MenuItem 不超过 1 个 MenuItem 祖先的节点:

XmlNodeList nodes = doc.SelectNodes("//MenuItem[count(ancestor::MenuItem) &lt; 2]");

或者你可以找到所有MenuItem节点:

XmlNodeList nodes = doc.SelectNodes("//MenuItem");

无论您选择如何选择它们,处理元素的方式都是相同的:

foreach (XmlElement elm in nodes)
{
   myList.Add(new[] { elm.GetAttribute("Header"), elm.GetAttribute("Name") });
}

因此,这建议了一种方法,您可以将其封装为带有您建议的签名的方法:

public static List<IEnumerable<string>>ReadXML(
   string filename, 
   string elementName, 
   IEnumerable<string> attributes, 
   bool searchSubNodes)
{
   XmlDocument d = new XmlDocument();
   d.Load(filename);
   string xpath = searchSubNodes ? "//" + elementName : "/*/elementName";
   List<IEnumerable<string>> results = new List<IEnumerable<string>>();
   foreach (XmlElement elm in d.SelectNodes(xpath))
   {
      var values = from name in attributes select elm.GetAttribute(name);
      result.Add(values.ToArray());
   }
   return result;
}

【讨论】:

  • 一点小事,看我的帖子。
【解决方案2】:

编辑:

要使用 XPath 选择具有给定节点名称和两个给定属性的所有节点,请使用以下 XPath 表达式(我不知道是否有更好的方法,但这是可行的):

//NodeName[@FirstAttributeName][@SecondAttributeName]

在代码中,您可以这样做(对于您示例中的 MenuItems)。我更新了代码以使您放入文档中的命名空间在前缀“x”下为人所知。此外,XPath 表达式已更新为在节点之前使用该前缀。

XPathDocument doc = new XPathDocument("data2.xml");
XPathNavigator nav = doc.CreateNavigator();
XmlNamespaceManager mgr = new XmlNamespaceManager(nav.NameTable);
mgr.AddNamespace("default", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
XPathNodeIterator iterator = nav.Select("//default:MenuItem[@Header][@Name]", mgr);

while (iterator.MoveNext())
{
    Console.Write(iterator.Current.GetAttribute("Header", ""));
    Console.Write(iterator.Current.GetAttribute("Name", ""));
    Console.Write(Environment.NewLine);
}

=====

您必须使用 XPath 吗?您可以像这样为映射到您的 XML 的 Menu/MenuItem 创建类:

public class Menu
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<MenuItem> SubItems { get; set; }
}

public class MenuItem
{
    public string Header { get; set; }
    public string Name { get; set; }
    public List<MenuItem> SubItems { get; set; }
}

然后让XMLSerializer 为您完成工作(它可以同时完成阅读和写作)。之后您可以对内存列表应用过滤(例如,使用 Linq-To-Objects)。

如果您想使用 XPath 执行此操作,此 Codeproject article 可能会有所帮助。

【讨论】:

  • 您好,我认为 XPath 是最好的解决方案。您的解决方案受 Menu 和 MenuItem 的限制,我需要一个可以放置任何节点的解决方案。 Menu 和 MenuItem 只是一个例子。问候。
  • 我没有意识到你只是举了一个你必须解析的例子,对不起。您可以扩展我的解决方案,但取决于您必须支持的节点数量,这将很快完成大量工作..
  • 哇。伟大的!它工作得很好,但是当在 xml 的顶部是这样的时候,它就不起作用了。该怎么办? schemas.microsoft.com/winfx/2006/xaml/test">
  • 这会为您的 xml 文档添加一个名称空间,这会阻止我之前的代码工作,因为 XPath 表达式中的节点不再可解析。我已经更新了代码以适应这种变化..
【解决方案3】:

两种解决方案都有效。但是在搜索到的节点之前是这样的:

<MenuItem x:Class="Test.Win"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/Test" <--this line make problems!
Header="Main" Name="Main">
<Menu id="1" Name="myMenu">
  <MenuItem Header="Header 1" Name="header1" />
  <MenuItem Header="Header 2" Name="header2">
    <MenuItem Header="subHeader 2.1" Name="header2_1">
      <MenuItem Header="subsubHeader 2.1.1" Name="header2_1_1" />
    </MenuItem>
  </MenuItem>
  <MenuItem Header="Header 3" Name="header3" />
</Menu>

</MyNode>

然后解决方案不起作用。如何仅删除架构行或跳过它或类似的东西

你们当中有人知道为什么以及如何解决这个问题吗?

问候!

【讨论】:

  • 您需要使用 XmlNameSpaceManager,请参见此处:stackoverflow.com/questions/972584/… 来自 Roberts 解决方案,它将是这样的...... var d = new XmlDocument(); var nameSpace = new XmlNamespaceManager(d.NameTable); nameSpace.AddNamespace("s", "schemas.microsoft.com/winfx/2006/xaml/presentation"); d.Load(filename); string xpath = searchSubNodes ? "//s:" + elementName : "/*/elementName"; var results = new List>();
  • 我收到错误:需要命名空间管理器或 XsltContext。此查询具有前缀、变量或用户定义的函数。
  • 你看到我的回答更新了吗?我添加了一个 XmlNamespaceManager 来解析您添加到 xml 的默认命名空间。这应该可以解决您遇到的错误。
  • 谢谢和yp。有用。还有一个问题。是否可以先将模式字符串读入变量,然后将其放入 addNamespace 方法?所以不需要我每次都自己放。
  • 这个问题可能会回答你的最后一个问题:stackoverflow.com/questions/122463/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多