很少种方法可以解决您的问题。
所以,我们有我们的 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 类,仅通过标记名称<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 或节点进行操作,而是使用某些类(例如 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;
由于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 文件。这里可以使用我们的类和属性属性(例如[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 元素,或者使用 XmlArray 和 XmlArrayItem 属性创建集合 - 取决于您的需要。还要注意null XML 节点或其值,并注意它,例如,Nullable<T>(例如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
}
希望它对您未来的目的有所帮助。