如何在 XML 中查找节点

How to find nodes in an XML

我使用 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>

如何获得Folio节点?

我试过:

  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"),返回值为空。

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

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

我可以遍历XML但是,我怎样才能直接获取元素呢?

谢谢 海梅

您可以尝试使用如下所示的 Xpath:

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

最好使用 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:

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 class,仅通过其标签名称查找后代节点 <Folio>:

// 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 或节点一起操作,而是与一些 class(例如 DTE)一起操作,这代表您的 XML 模式。我不确定我是否完全理解您的 XML 结构,但无论如何它都可以用作示例。

因此我们创建 classes,它们代表我们的 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 class 及其 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 as 属性 of IdDoc class, 即 属性 of Encabezado class ,这又是 属性 of Documento class。记住可能的 null 结果让我们使用,例如 null-propagation:

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

由于Documentacion是一个List<Documento>——我们再次使用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 文件。我们的 class 和 properties 属性(例如 [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 字符串的解析可以替换为加载 XML 文件(通过 FileStreams)。当然,您可以使用其他属性编辑 DTE(和子)classes,并将它们标记为 XML 属性或 XML 元素,或者使用 XmlArray 和 [=45 创建集合=] 属性 - 随便什么,取决于你的需要。还要注意 null XML 节点或其值,并注意它,例如 Nullable<T>(例如 long?DateTime?)、IsNullable 属性 的 XML 属性和 属性 setter:

处的某种值验证
 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
 }

希望对您以后的目的有所帮助。