-1

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

我在 .Net 4.0 中有以下 C# 代码。它工作正常且可读性强。performance在不牺牲的情况下有没有更好的代码readability

代码

// 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. 生成 c# 对象代码并从 xml 文档为其属性赋值
  2. 使用注释将 LINQ 转换为 XSLT 样式的 XML 树 - Eric White
  3. XSLT 或 Linq to XML 的优势
4

1 回答 1

1

关于我唯一真正推荐的是将StatusDetail节点也存储在一个列表中,只需抓取一次,然后您可以通过第二个 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
于 2013-03-20T13:13:10.693 回答