【问题标题】:Reading XML to create objects读取 XML 以创建对象
【发布时间】:2020-12-05 16:53:12
【问题描述】:

说明

我有一个表示 XML 文档的字符串,我想解析并创建以下类型的对象集合:

public class Invoice
{
    public const string XmlName = "Invoice";

    public int id { get; set; }
    public string title { get; set; }
    public DateTime timestamp { get; set; }
    public bool paid { get; set; }
    public IList<InvoiceItem> items { get; set; }

    public Invoice() { this.items = List<InvoiceItem>(); }

    public double getTotal()
    {
        if (items == null)
            return 0;
        
        double total = 0;

        foreach (InvoiceItem item in this.items)
            total += item.amount;

        return total;
    }
}

public class InvoiceItem
{
    public const string XmlName = "InvoiceItem";

    public string description { get; set; }
    public double amount { get; set;}
}

用于测试目的的 XML 如下。我还没有创建架构,但本质上,它是Invoices 的列表,每个包含零个或多个InvoiceItems:

<?xml version="1.0" encoding="UTF-8"?>
<invoices>
    <invoice id="1", title="Aug 2020", timestamp="16/08/2020 09:01:29 AM", paid="true">
        <item desc="item 1 in invoice 1", amount="50"/>
        <item desc="item 2 in invoice 1", amount="50"/>
    </invoice>
    <invoice id="2", title="Sep 2020", timestamp="16/09/2020 09:01:29 AM", paid="false">
        <item desc="item in invoice 2", amount="100"/>
    </invoice>
</invoices>

代码

以下是用于读取 XML 并生成所需输出的方法:

public IEnumerable<Invoice> readXML()
{
    XmlReaderSettings xmlReaderSettings = new XmlReaderSettings();
    xmlReaderSettings.IgnoreWhitespace = true;
    xmlReaderSettings.IgnoreComments = true;
    xmlReaderSettings.IgnoreProcessingInstructions = true;

    using (XmlReader reader = XmlReader.Create(new System.IO.StringReader(this._fileHandler.getXML()), xmlReaderSettings))
    {
        reader.MoveToContent(); // skip over the XML declaration, should move to the invoices start tag

        reader.ReadStartElement("invoices"); // move to the next element, should be start tag for an invoice
        while (reader.NodeType == XmlNodeType.Element)
        {
            if (reader.Name == Invoice.XmlName)
            {
                // hit an invoice start tag

                // read it
                Invoice invoice = new Invoice();

                if (reader.MoveToAttribute("id"))
                    invoice.id = reader.ReadContentAsInt();

                if (reader.MoveToAttribute("title"))
                    invoice.title = reader.ReadContentAsString();

                if (reader.MoveToAttribute("timestamp"))
                    invoice.timestamp = DateTime.Parse(reader.ReadContentAsString());

                if (reader.MoveToAttribute("paid"))
                    invoice.paid = reader.ReadContentAsBoolean();

                reader.ReadStartElement("items"); // move to next element, should be items start tag
                    while (reader.NodeType == XmlNodeType.Element)
                    {
                        reader.Read();

                        InvoiceItem invoiceItem = new InvoiceItem();

                        if (reader.Name == InvoiceItem.XmlName)
                        {

                            if (reader.MoveToAttribute("desc"))
                                invoiceItem.description = reader.ReadContentAsString();

                            if (reader.MoveToAttribute("amount"))
                                invoiceItem.amount = reader.ReadContentAsDouble();

                            invoice.items.Add(invoiceItem);
                        }
                        else
                        {
                            throw new XmlException("Unexpected XML node: " + reader.Name);
                        }
                    }

                    yield return invoice;
                    }

                else
                {
                    throw new XmlException("Unexpected XML node: " + reader.Name);
                }
            }
       }
}

在测试中,它会产生一个异常:

System.Xml.XmlException : Name cannot begin with the ',' character, hexadecimal value 0x2C. Line 3, position 17.

这是什么原因造成的?有关如何正确将此 XML 解析为所需对象列表的一些指导会很有帮助。

【问题讨论】:

    标签: c# .net xml object


    【解决方案1】:

    这不是一个有效的 xml 字符串。 ',' 字符无效。尝试以下方法。

    在 xml 中,属性由空格分隔,这就是验证器警告您的内容。行和字符是第一次出现的逗号。

    <?xml version="1.0" encoding="UTF-8"?>
    <invoices>
        <invoice id="1" title="Aug 2020" timestamp="16/08/2020 09:01:29 AM" paid="true">
            <item desc="item 1 in invoice 1" amount="50"/>
            <item desc="item 2 in invoice 1" amount="50"/>
        </invoice>
        <invoice id="2" title="Sep 2020" timestamp="16/09/2020 09:01:29 AM" paid="false">
    </invoices>
    

    您还需要将以下行改为小写:

    public const string XmlName = "invoice";
    

    【讨论】:

    • 是的,我错误地在标签中添加了逗号。我修复了它,但现在它产生:System.Xml.XmlException : Unexpected XML node: invoice
    【解决方案2】:

    您的 xml 包含 invoiceitem 节点。因此,您需要更改XmlName 常量的值。注意:为什么要制作它们?

    我更改了属性和方法的命名大小写以匹配普遍接受的样式。

    我还用decimal 替换了double - 不要用实数来计算金钱。

    public class Invoice
    {
        public const string XmlName = "invoice";
    
        public int Id { get; set; }
        public string Title { get; set; }
        public DateTime Timestamp { get; set; }
        public bool Paid { get; set; }
        public IList<InvoiceItem> Items { get; }
    
        public Invoice() { Items = new List<InvoiceItem>(); }
    
        public decimal GetTotal()
        {
            if (Items == null)
                return 0;
    
            decimal total = 0;
    
            foreach (InvoiceItem item in Items)
                total += item.Amount;
    
            return total;
        }
    }
    
    public class InvoiceItem
    {
        public const string XmlName = "item";
    
        public string Description { get; set; }
        public decimal Amount { get; set; }
    }
    

    XmlReader 类包含许多方便的方法。在这种情况下,我们需要ReadToFollowingReadToNextSibling

    这里是解析代码

    public IEnumerable<Invoice> readXML()
    {
        XmlReaderSettings xmlReaderSettings = new XmlReaderSettings();
        xmlReaderSettings.IgnoreWhitespace = true;
        xmlReaderSettings.IgnoreComments = true;
        xmlReaderSettings.IgnoreProcessingInstructions = true;
    
        using (var reader = XmlReader.Create(new StringReader(...), xmlReaderSettings))
        {
            while (reader.ReadToFollowing(Invoice.XmlName))
            {
                Invoice invoice = new Invoice();
    
                if (reader.MoveToAttribute("id"))
                    invoice.Id = reader.ReadContentAsInt();
    
                if (reader.MoveToAttribute("title"))
                    invoice.Title = reader.ReadContentAsString();
    
                if (reader.MoveToAttribute("timestamp"))
                    invoice.Timestamp = DateTime.Parse(reader.ReadContentAsString());
    
                if (reader.MoveToAttribute("paid"))
                    invoice.Paid = reader.ReadContentAsBoolean();
    
                if (reader.ReadToFollowing(InvoiceItem.XmlName))
                {
                    do
                    {
                        InvoiceItem invoiceItem = new InvoiceItem();
    
                        if (reader.MoveToAttribute("desc"))
                            invoiceItem.Description = reader.ReadContentAsString();
    
                        if (reader.MoveToAttribute("amount"))
                            invoiceItem.Amount = reader.ReadContentAsDecimal();
    
                        invoice.Items.Add(invoiceItem);
    
                    } while (reader.ReadToNextSibling(InvoiceItem.XmlName));
                }
    
                yield return invoice;
            }
        }
    }
    

    重要!

    请务必在DateTime.Parse 方法中指定FormatProvider。因为它可以在具有不同设置的计算机上以不同的方式工作。

    或者使用指定格式的DateTime.ParseExact方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多