【问题标题】:How to find nodes in an XML如何在 XML 中查找节点
【发布时间】:2022-01-17 08:28:13
【问题描述】:

我使用xml.Load("myfile.xml"); 加载了以下XML 文件,其中xml 的类型为XmlDocument

<?xml version="1.0" encoding="ISO-8859-1"?>
    <DTE xmlns="http://www.sii.cl/SiiDte" version="1.0">
        <Documento ID="E000000005T033F0114525415">
            <Encabezado>
                <IdDoc>
                    <TipoDTE>33</TipoDTE>
                    <Folio>114525415</Folio>
                    <FchEmis>2021-11-02</FchEmis>
                    <FmaPago>1</FmaPago>
                    <FchVenc>2021-11-02</FchVenc>
                </IdDoc>
            </Encabezado>
        </Documento>
    </DTE>

如何获得Folionode?​​p>

我试过了:

  xml.DocumentElement.SelectSingleNode("/DTE/Documento/Encabezado/IdDoc/Folio");
  xml.DocumentElement.SelectNodes("DTE/Documento/Encabezado/IdDoc/Folio")
  xml.DocumentElement.SelectSingleNode("//DTE/Documento/Encabezado/IdDoc/Folio");
  xml.DocumentElement.SelectSingleNode("/Documento/Encabezado/IdDoc/Folio");
  xml.DocumentElement.SelectSingleNode("Documento/Encabezado/IdDoc/Folio");
  xml.DocumentElement.SelectSingleNode("/Encabezado/IdDoc/Folio");
  xml.DocumentElement.SelectNodes("/DTE/Documento/Encabezado/IdDoc/Folio")

当我调试xml.DocumentElement 时,我看到元素是DTE,所以我认为xml.DocumentElement.SelectSingleNode("Documento/Encabezado/IdDoc/Folio") 应该这样做。

当我得到xml.DocumentElement.FirstChild 时,我得到Documento 节点。

使用xml.DocumentElement.FirstChild.FirstChild 我得到Encabezado 节点。

使用xml.DocumentElement.FirstChild.FirstChild.FirstChild 我得到IdDoc 节点。

如果我使用xml.DocumentElement.FirstChild.FirstChild.FirstChild.SelectSingleNode("Folio"),返回值为null。

如果我使用xml.DocumentElement.FirstChild.FirstChild.FirstChild.ChildNodes,我会得到 5 个元素。

然后我可以使用xml.DocumentElement.FirstChild.FirstChild.FirstChild.ChildNodes[1].InnerText 来获取Folio 的值。

我可以遍历XML,但是如何直接获取元素呢?

谢谢 詹姆

【问题讨论】:

    标签: c# xml xml-parsing xmldocument


    【解决方案1】:

    最好使用 LINQ to XML API 来完成您的任务。它自 2007 年起在 .Net Framework 中可用。

    提供的 XML 具有默认命名空间。它需要声明和使用,否则找不到任何 XML 元素。

    c#

    void Main()
    {
        const string filename = @"e:\Temp\jstuardo.xml";
    
        XDocument xdoc = XDocument.Load(filename);
        XNamespace ns = xdoc.Root.GetDefaultNamespace();
    
        string Folio = xdoc.Descendants(ns + "Folio")
          .FirstOrDefault()?.Value;
    
        Console.WriteLine("Folio='{0}'", Folio);
    }
    

    输出

    Folio='114525415'
    

    【讨论】:

    • 谢谢!我不知道带有 XML 的 LINQ。最后,我可以到达我需要的所有特定节点。
    【解决方案2】:

    您可以尝试使用如下 Xpath:

    XmlDocument doc = new XmlDocument();
    doc.Load("myfile.xml");
    var node= doc.SelectSingleNode("Documento/Encabezado/IdDoc/[Folio='"114525415"']");
    

    【讨论】:

      【解决方案3】:

      很少种方法可以解决您的问题。

      所以,我们有我们的 XML:

      const string MyXML = @"<?xml version=""1.0"" encoding=""ISO-8859-1""?>
                             <DTE xmlns=""http://www.sii.cl/SiiDte"" version=""1.0"">
                                 <Documento ID=""E000000005T033F0114525415"">
                                     <Encabezado>
                                         <IdDoc>
                                             <TipoDTE>33</TipoDTE>
                                             <Folio>114525415</Folio>
                                             <FchEmis>2021-11-02</FchEmis>
                                             <FmaPago>1</FmaPago>
                                             <FchVenc>2021-11-02</FchVenc>
                                         </IdDoc>
                                     </Encabezado>
                                 </Documento>
                             </DTE>";
      

      我们需要得到Folio 节点(确切地说是节点,而不仅仅是值)。我们可以使用:


      XmlNamespaceManager:

      通过 XPath 中的 XML 命名空间 (xmlns) 别名查找后代节点:

      // Creating our XmlDocument instance
      var xmlDocument = new XmlDocument();
      xmlDocument.LoadXml(MyXML);
      
      // Initializing XmlNamespaceManager and providing our xmlns with 'SiiDte' alias:
      var xmlNamespaceManager = new XmlNamespaceManager(xmlDocument.NameTable);
      xmlNamespaceManager.AddNamespace("SiiDte", "http://www.sii.cl/SiiDte");
      
      // Declaring our simple shiny XPath:
      var xPath = "descendant::SiiDte:Folio";
      
      // If we need single (first) element:
      var folio = xmlDocument.DocumentElement.SelectSingleNode(xPath, xmlNamespaceManager);
      // If we need all Folios:
      var folios = xmlDocument.DocumentElement.SelectNodes(xPath, xmlNamespaceManager);
      

      XDocument 及其后代:

      System.Xml.Linq 命名空间及其XDocument 类,仅通过标记名称&lt;Folio&gt; 查找后代节点:

      // If we need single (first) element:
      var folio = XDocument.Parse(MyXML)
                           .Descendants()
                           .FirstOrDefault(x => x.Name.LocalName == "Folio");
                           // Add System.Linq using to access FirstOrDefault extension method
      // If we need all Folios - just replacing FirstOrDefault with Where extension method:
      var folios = XDocument.Parse(MyXML)
                            .Descendants()
                            .Where(x => x.Name.LocalName == "Folio"); // and .ToList() if you need
      // Or we can use also our XML namespace to filter Descendants:
      var ns = (XNamespace)"http://www.sii.cl/SiiDte";
      var folios = XDocument.Parse(MyXML).Descendants(ns + "Folio");
      

      反序列化:

      不使用 XML 或节点进行操作,而是使用某些类(例如 DTE),它代表您的 XML 模式。我不确定我是否完全理解您的 XML 结构,但无论如何它都可以用作示例。

      所以我们创建了我们的类,它们代表了我们的 XML:

      [Serializable, XmlRoot(ElementName = nameof(DTE), Namespace = "http://www.sii.cl/SiiDte")]
      public class DTE
      {
          [XmlAttribute("version")]
          public string Version { get; set; }
      
          [XmlElement(nameof(Documento))]
          public List<Documento> Documentacion { get; set; }
      }
      [Serializable]
      public class Documento
      {
          [XmlAttribute(nameof(ID))]
          public string ID { get; set; }
          [XmlElement(nameof(Encabezado))]
          public Encabezado Encabezado { get; set; }
      }
      [Serializable]
      public class Encabezado
      {
          [XmlElement(nameof(IdDoc))]
          public IDDoc IdDoc { get; set; }
      }
      [Serializable]
      public class IDDoc
      {
          [XmlElement(nameof(TipoDTE))]
          public int TipoDTE { get; set; }
          [XmlElement(nameof(Folio))]
          public long Folio { get; set; }
          [XmlElement(nameof(FchEmis))]
          public DateTime FchEmis { get; set; }
          [XmlElement(nameof(FmaPago))]
          public int FmaPago { get; set; }
          [XmlElement(nameof(FchVenc))]
          public DateTime FchVenc { get; set; }
      }
      

      现在我们可以使用XmlSerializer 类及其Deserialize 方法轻松创建DTE 对象:

      // Declaring our DTE object
      var dte = (DTE)null;​
      using (var reader = new StringReader(MyXML))
      {
          dte = (DTE)new XmlSerializer(typeof(DTE)).Deserialize(reader);
      }
      

      现在我们可以得到Folio作为IdDoc类的属性,它是Encabezado类的属性,又是Documento类的属性。记住可能的null结果让我们使用,例如null-propagation

      var folio = dte?.Documentacion.FirstOrDefault()?.Encabezado?.IdDoc?.Folio;
      

      由于DocumentacionList&lt;Documento&gt; - 我们再次使用FirstOrDefault(也可以使用ElementAtOrDefault(0))来“模仿”SelectSingleNode。对于所有Folios,我们可以使用Select(也可以使用mull-propagation):

      var folios = dte?.Documentacion.Select(x => x?.Encabezado?.IdDoc?.Folio);
      

      当然,我们可以根据需要编辑属性或添加新属性:

      // Edit
      if (dte?.Documentacion.FirstOrDefault() is Documento documento)
          documento.Encabezado.IdDoc.Folio = 112233445566;
      
      // or create and add new
      var newDocumento = new Documento
      {
          ID = "SomeID",
          Encabezado = new Encabezado
          {
              IdDoc = new IDDoc
              {
                  TipoDTE = 123,
                  Folio = 112233445566,
                  FmaPago = 1,
                  FchEmis = DateTime.Now,
                  FchVenc = DateTime.Now.AddDays(1)
              }
          }
      };
      dte.Documentacion.Add(newDocumento);
      

      最后使用序列化保存回 XML 文件。这里可以使用我们的类和属性属性(例如[Serializable][XmlElement] 等),它们指定每个属性应该如何命名或在 XML 中表示:

      using (var xmlWriter = XmlWriter.Create("My.xml", 
                                              new XmlWriterSettings 
                                              { 
                                                  Encoding = Encoding.GetEncoding("ISO-8859-1"),
                                                  Indent = true 
                                              }))
      {
          // We remove default XSI, XSD namespaces and leave only our custom:
          var xmlSerializerNamespaces = new XmlSerializerNamespaces();
          xmlSerializerNamespaces.Add("", "http://www.sii.cl/SiiDte");
      
          // and saving our DTE object to XML file.
          xmlSerializer.Serialize(xmlWriter, dte, xmlSerializerNamespaces);
      }
      

      备注

      当然,如果需要,可以用加载 XML 文件(由FileStreams)代替对 XML 字符串的解析。当然,您可以编辑具有其他属性的 DTE(和子)类,并将它们标记为 XML 属性或 XML 元素,或者使用 XmlArrayXmlArrayItem 属性创建集合 - 取决于您的需要。还要注意null XML 节点或其值,并注意它,例如,Nullable&lt;T&gt;(例如long?DateTime?)、IsNullable XML 属性的属性和某种属性值验证二传手:

       private long _folio;
       [XmlElement(nameof(Folio), IsNullable = true)]
       public long? Folio
       {
              get => _folio;
              set => _folio = value ?? 0L; // Null-coalescing with default fallback value of 0L
       }
      

      希望它对您未来的目的有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多