用于创建对象和从XML更新其属性的更好代码

时间:2013-03-20 12:33:57

标签: c# xml linq

我有多个Invoice元素的XML输入。我从这些元素创建发票对象。根据发票元素的位置,我们需要分配序列号并从不同的元素中找到相应的消息 - StatusMsg。

我在.Net 4.0中有以下C#代码。它工作正常,相当可读。在没有牺牲performance的情况下,readability是否有更好的代码?

CODE

// Create a collection of invoice elements
var invoiceEntities = xDoc.Descendants("Invoice")
              .Select(x => new Invoice
               {
                  Vendor = x.Element("Vendor") == null ? String.Empty : x.Element("Vendor").Value.Trim(),
                  Amount = x.Element("Amount") == null ? String.Empty : x.Element("Amount").Value.Trim()
               });

List<Invoice> invoices = invoiceEntities.ToList();

//Iterate all entities for finding corresponding message element and update the entity's Message

int count = 0;
foreach (Invoice entity in invoices)
{
           count++;

           //Dynamic XPath statement
           string messagePath = @"Status/StatusMsg/StatusDetail/Sequence[text()=" + count.ToString() + "]/../Message";
           var statusDetails = xDoc.XPathSelectElements(messagePath).FirstOrDefault();
           if (statusDetails != null)
           {
               entity.Message = statusDetails.Value;
               entity.Sequence = count;
           }

  }

实体

public class Invoice
{
    public string Vendor { get; set; }
    public string Amount { get; set; }
    public string Message { get; set; }
    public int Sequence { get; set; }
}

XML

  XDocument xDoc = XDocument.Parse(@"  
          <Status>
                <StatusMsg>
                    <StatusType>INVOICE</StatusType>
                    <StatusCode>READYPAY</StatusCode>
                    <StatusTimestamp>2013-03-19T21:20:54Z</StatusTimestamp>

                    <StatusDetail>
                        <Sequence test=""K"">  2  </Sequence>
                        <Message>STL MESSAGE </Message>
                    </StatusDetail>

                    <StatusDetail>
                        <Sequence test=""1"">  1  </Sequence>
                        <Message>AKP MESSAGE</Message>
                    </StatusDetail>

                    <StatusDetail>
                        <Sequence> 1 </Sequence>
                        <Message>CC</Message>
                    </StatusDetail>

                </StatusMsg>
                <Invoices> 

                    <Invoice>
                        <Vendor>
                         AKP LLC
                        </Vendor>
                        <Amount>
                         100
                        </Amount>
                    </Invoice>

                    <Invoice>
                        <Vendor>
                         STL Inc
                        </Vendor>
                        <Amount>
                         20950
                        </Amount>
                    </Invoice>

                </Invoices>
            </Status>
           ");

参考

  1. Generate c# object code and assign values to its properties from an xml document
  2. Use Annotations to Transform LINQ to XML Trees in an XSLT Style - Eric White
  3. Advantages of XSLT or Linq to XML

1 个答案:

答案 0 :(得分:1)

关于我唯一真正建议的是将StatusDetail节点存储在List中,只需抓取它们一次,然后你可以通过第二个linq语句引用列表来过滤序列。这可能比简单地构建和重用XPath字符串更慢。

var Details = xDoc.Descendants("StatusDetail").ToList();

...

var statusDetail = Details.Where(a => a.Sequence == count).FirstOrDefault();

作为一个挑剔的开发点,它通常建议在做奇怪的连字符串时使用String.Format ......关于后端代码的效率更高......

string messagePath = String.Format("Status/StatusMsg/StatusDetail/Sequence[text()={0}]/../Message", count);

另一种选择,您已经在构建一个匿名类型,没有真正的理由不能将计数构建到Invoice选项中。这至少可以使您免于在循环中单独声明和维护计数。

int count = 1;

var invoiceEntities = xDoc.Descendants("Invoice")
          .Select(x => new Invoice
           {
              Vendor = x.Element("Vendor") == null ? String.Empty : x.Element("Vendor").Value.Trim(),
              Amount = x.Element("Vendor") == null ? String.Empty : x.Element("Amount").Value.Trim(),
              Index = count++
           });//yes that works, I tested it because even I wasn't sure, but Index is correct and different for each element