简体   繁体   English

如何使用xpath从C#中的xml文件读取/写入节点和子节点?

[英]How to read/write nodes and child nodes from xml file in c# maybe using xpath?

Maybe somebody could help me. 也许有人可以帮助我。 I need a two methods. 我需要两种方法。

The first should open a xml file and get all nodes with the given parameter ie.: 第一个应该打开一个xml文件,并获取具有给定参数的所有节点,即:

XML file (file.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>

So, now I need to get the values from the xml with a method like this: 因此,现在我需要使用以下方法从xml中获取值:

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

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

and this would return a list of two strings like: 这将返回两个字符串的列表,例如:

"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"

THIS was the reading part and now the writting part: 这是阅读部分,现在是写作部分:

filename is the same like above file.xml. 文件名与上面的file.xml相同。 public static void WriteXML(string filename, string node, List attributes); 公共静态无效WriteXML(字符串文件名,字符串节点,列表属性);

now lets say the file.xml has empty header attributes like this: 现在让我们说file.xml具有空的标题属性,如下所示:

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

And I need to put values into the headers, the finall result should look like this: 我需要将值放入标头中,最终结果应如下所示:

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

Is something like this possible??? 这样的事情可能吗??? C# gurus and other people who knows how to do this PLEASE PLEASE help me! C#专家和其他知道如何执行此操作的人请帮助我! I don't know how to do it. 我不知道该怎么做。

Best regards! 最好的祝福!

You can do all of the searching using XPath. 您可以使用XPath进行所有搜索。

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

In this case, nodes will contain all of the MenuItem elements that are a child of the top-level Menu element. 在这种情况下, nodes将包含作为顶级Menu元素的子元素的所有MenuItem元素。

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

In this case, nodes will contain all MenuItem elements that are children either of Menu or of a MenuItem that's a child of Menu . 在这种情况下, nodes将包含所有MenuItem是儿童或者元素Menu或的MenuItem这是一个儿童Menu

You can also find all MenuItem nodes that have no more than 1 MenuItem ancestor: 您还可以找到所有不超过1个MenuItem祖先的MenuItem节点:

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

Or you can just find all MenuItem nodes: 或者,您可以找到所有MenuItem节点:

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

However you choose to select them, processing the elements works the same way: 无论您选择选择它们,处理元素的方式都相同:

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

So this suggests a way that you could encapsulate this as a method with the signature you suggest: 因此,这建议您可以使用建议的签名将其封装为方法:

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;
}

EDIT: 编辑:

To use XPath to select all nodes that have a given node name AND two given attributes use the following XPath expression (I don't know if there are better ways to do it but this works): 要使用XPath选择具有给定节点名和两个给定属性的所有节点,请使用以下XPath表达式(我不知道是否有更好的方法可以做到这一点,但这是可行的):

//NodeName[@FirstAttributeName][@SecondAttributeName]

In code you could do it like that (for the MenuItems from your example). 在代码中,您可以这样做(对于示例中的MenuItems)。 I updated the code to make the namespace you put in your document known under the prefix "x". 我更新了代码,使放置在文档中的名称空间在前缀“ x”下成为已知。 Also the XPath expression has been updated to use that prefix before the node. 此外,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);
}

===== =====

Do you have to use XPath? 您必须使用XPath吗? You could just create classes for Menu/MenuItem that map to your XML like this: 您可以只为Menu / MenuItem创建映射到XML的类,如下所示:

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; }
}

And then have an XMLSerializer do the work for you (it can do both, reading and writing). 然后让XMLSerializer为您完成工作(它可以同时进行读取和编写)。 You could afterwards apply filtering to the in-memory lists (eg using Linq-To-Objects). 之后,您可以将过滤应用于内存列表(例如,使用Linq-To-Objects)。

If you want to do it using XPath this Codeproject article might be helpful. 如果您想使用XPath做到这一点,这篇Codeproject文章可能会有所帮助。

Both solutions works. 两种解决方案均有效。 But when before the searched node is somethnig like this: 但是在搜索到的节点之前是这样的:

<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>

then the solutions doesn't work. 那么解决方案就行不通了。 How to remove only the schema line or just skip it or somthing like that 如何仅删除架构行或仅跳过它或类似内容

Do somebody of you maybe know why and how to solve also this problem? 你们中的某人是否知道为什么以及如何解决此问题?

Regards! 问候!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM