在C#中从XML字符串中读取属性的最佳方法是什么?

7

我有以下字符串形式的xml:

<cfdi:Comprobante version="3.0"
                  xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv3.xsd"
                  serie="A"
                  folio="6"
                  fecha="2011-07-22T13:51:42"
                  formaDePago="Pago en una sola exhibición"
                  sello="XlSJYAxauwYbI"
                  noCertificado="00001000000101242210"
                  certificado="YtEQOHw02OGx6E="
                  condicionesDePago="Paguese a mas tardar el 21/08/2011."
                  subTotal="123"
                  Moneda="MXN"
                  total="123"
                  tipoDeComprobante="ingreso">
  <cfdi:Complemento>
    <tfd:TimbreFiscalDigital FechaTimbrado="2011-07-22T13:51:47"
                             UUID="41C8A54F-4956-1BAD-F2CB-48E8343918FD"
                             noCertificadoSAT="00001000000102616613"
                             selloCFD="wrwerewe"
                             version="1.0"
                             xsi:schemaLocation="http://www.sat.gob.mx/TimbreFiscalDigital http://www.sat.gob.mx/sitio_internet/timbrefiscaldigital/TimbreFiscalDigital.xsd"/>
  </cfdi:Complemento>
</cfdi:Comprobante>

我想读取节点tfd:TimbreFiscalDigital内的属性UUID,我想知道如何在c#中实现这一点。请注意,此xml位于字符串中,而不是文件中(我们的提供者web服务将xml作为字符串返回,这不是我们的问题)。
注:我可以使用Linq或任何其他库,这不是问题。
谢谢!!

为了更易读,最好将XML格式化为多行。 - cordialgerm
2
它不是格式良好的XML。具体来说,xml命名空间前缀xsitfdcfdi没有定义。如果您想使用.NET中的任何XML库解析XML,则首先需要使其成为格式良好的。如果他们没有发送正确的XML,则可能需要手动修复。 - Cheeso
3个回答

2

我将其包装在一个声明命名空间的根节点中。我还使用XPath查询节点。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Xml;

class Program
{
    static void Main(string[] args)
    {

        var doc = @"
<Root xmlns:xsi='http://someuri' xmlns:cfdi='http://someuri2' xmlns:tfd='http://someuri3'>
<cfdi:Comprobante version='3.0'
                  xsi:schemaLocation='http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv3.xsd'
                  serie='A'
                  folio='6'
                  fecha='2011-07-22T13:51:42'
                  formaDePago='Pago en una sola exhibición'
                  sello='XlSJYAxauwYbI'
                  noCertificado='00001000000101242210'
                  certificado='YtEQOHw02OGx6E='
                  condicionesDePago='Paguese a mas tardar el 21/08/2011.'
                  subTotal='123'
                  Moneda='MXN'
                  total='123'
                  tipoDeComprobante='ingreso'>
  <cfdi:Complemento>
    <tfd:TimbreFiscalDigital FechaTimbrado='2011-07-22T13:51:47'
                             UUID='41C8A54F-4956-1BAD-F2CB-48E8343918FD'
                             noCertificadoSAT='00001000000102616613'
                             selloCFD='wrwerewe'
                             version='1.0'
                             xsi:schemaLocation='http://www.sat.gob.mx/TimbreFiscalDigital http://www.sat.gob.mx/sitio_internet/timbrefiscaldigital/TimbreFiscalDigital.xsd'/>
  </cfdi:Complemento>
</cfdi:Comprobante>
</Root>";

        var uuid = XDocument.Parse(doc)
            var uuid = XDocument.Parse(doc)
            .XPathSelectElement("//*[local-name() = 'TimbreFiscalDigital']")
            .Attribute("UUID").Value;

        // Work with uuid

        Console.Read();
    }
}

将额外的根元素包装起来是个好主意。但是如果不使用XML命名空间,你的XPath将无法工作。 - Cheeso
没错,它标记了一个错误:“System.Xml.Linq.XDocument”不包含“XPathSelectElement”的定义,并且找不到接受类型为“System.Xml.Linq.XDocument”的第一个参数的扩展方法“XPathSelectElement”(您是否缺少使用指令或程序集引用?) - franko_camron
@franko_camron - 如果你注意到我的代码,你需要在你的代码中加入 using System.Xml.XPath;。否则,这段代码完美地运行。由于缺少命名空间,所以被接受的答案不会起作用。 - TheCloudlessSky

1

我发现Linq的XDocument和相关类特别简单明了易于使用:

string uuid = XDocument.Parse(xmlString)
    .Descendants("TimbreFiscalDigital")
    .Attributes("UUID")
    .First()
    .Value;

简短明了,但需要考虑XML命名空间。 - Cheeso

1

因为你有命名空间前缀,所以你将不得不使用 XNamespace 实例来帮助你引用元素。

// We use these to establish our namespace prefixes
XNamespace cfdi = @"http://www.sat.gob.mx/cfd/3";
XNamespace tfd = @"http://www.sat.gob.mx/TimbreFiscalDigital";

var xdoc = XDocument.Parse(xml);

// Walk down the XML tree to tfd:TimbreFiscalDigital
var elt = xdoc.Element(cfdi + "Comprobante")
              .Element(cfdi + "Complemento")
              .Element(tfd + "TimbreFiscalDigital");
// Alternately
// var elt = xdoc.Descendants(tfd + "TimbreFiscalDigital")
//               .First();

var uuid = (string)elt.Attribute("UUID");

// You can convert attributes and element values to lots of built-in types
// See the Explicit Conversions for XAttribute and XElement on MSDN
var date = (DateTime)elt.Attribute("FechaTimbrado");

更多阅读:


这段代码无法编译。前两行应该是 XNamespace.Get("http... - TheCloudlessSky
谢谢,这会教训我在Macbook上输入的方式。我已经切换到首选形式了。 - user7116
我认为现在它将会编译通过,但是它通不过“运行”,因为原问题中包含的xml格式不正确。它使用了未定义的命名空间前缀。因此,你的XDocument.Parse()将会报错。 - Cheeso
@Cheeso:在我解决问题之后,我看到了你的评论。我没有意识到示例片段是完整的XML。好吧,是时候添加一些补丁式的黑客技巧了。 - user7116
在检查了所有的解决方案后,结果发现这是唯一一个对我有效的解决方案,因为它涉及到命名空间。也许它不是最整洁的解决方案,但它能够工作!谢谢啊! - franko_camron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接