如何在 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 文件(通过 FileStream
s)。当然,您可以使用其他属性编辑 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
}
希望对您以后的目的有所帮助。
我使用 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 文件(通过 FileStream
s)。当然,您可以使用其他属性编辑 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
}
希望对您以后的目的有所帮助。