【问题标题】:Get Element Before Self在自我之前获取元素
【发布时间】:2013-10-17 13:02:32
【问题描述】:

如果当前的“名称”元素为空或为空,它应该采用前一个元素的“名称”值。第一个元素名称永远不会为 null 或为空。

我需要在当前“名称”元素值为空或为空的情况下获取之前的元素“名称”值:

List<ScripTbl> ScripData = ScrpHldNode.Descendants(ns + "Scrp").Select(x => new ScripTbl
{
    ID = sID,
    //need help with the line below....
    ScripShare = (!string.IsNullOrEmpty((String)x.Element(ns + "Name")) ? (String)x.Element(ns + "Name") : (x.ElementsBeforeSelf().Count() > 0 ? (String)x.ElementsBeforeSelf().Last().Element(ns + "Name") : "")),
    ScripOpenBalanceDue = (String)x.Element(ns + "OpBal"),
    ScripClosingBalance = (String)x.Element(ns + "ClBal"),
    ScripTransaction = (String)x.Element(ns + "Tran"),
    ScripMovement = (String)x.Element(ns + "Move"),
    ScripFooter = sScripFooter,
    Seq = x.ElementsBeforeSelf().Count() + 1,
    ScripSectorCode = (String)x.Element(ns + "SecCde"),
    ScripClosingPrice = (String)x.Element(ns + "ClPrce"),
    SplitIndicator = (!string.IsNullOrEmpty((String)x.Element(ns + "Name")) ? (x.ElementsBeforeSelf().Count() > 0 && (String)x.Element(ns + "Name") == (String)x.ElementsBeforeSelf().Last().Element(ns + "Name") ? "1" : "0") : "0")
}).ToList();

和 XML:

<ScrpHld xmlns="http://www.website.co.za/namespace">
<Scrp>
    <Name>Company Name Number 1</Name>
    <SecCde>1366J</SecCde>
    <Tran>SOLD</Tran>
    <OpBal>0</OpBal>
    <Move>-2000</Move>
</Scrp>
<Scrp>
    <Tran>ELECTRONIC SETTLEMENT</Tran>
    <Move>2000</Move>
    <ClPrce>25045.00</ClPrce>
    <ClBal>0</ClBal>
</Scrp>
<Scrp>
    <Name>Company Name Number 2</Name>
    <SecCde>1313J</SecCde>
    <Tran>SOLD</Tran>
    <OpBal>10000</OpBal>
    <Move>-90500</Move>
</Scrp>
<Scrp>
    <Tran>ELECTRONIC SETTLEMENT</Tran>
    <Move>80500</Move>
    <ClPrce>3392.00</ClPrce>
    <ClBal>0</ClBal>
</Scrp> 
</ScrpHld>

【问题讨论】:

    标签: c# xml lambda linq-to-xml


    【解决方案1】:

    使用PreviousNode而不是ElementsBeforeSelf,会更容易。我分成多行只是为了增加答案的可读性。

    该行应该与此类似

    ScripShare = (x.Element(ns + "Name") != null 
                     ? (string)x.Element(ns + "Name")
                     : (x.PreviousNode != null
                              ? ((XElement)x.PreviousNode).Element(ns + "Name").Value
                              : string.Empty)),
    

    如果您的 xml 将在元素中包含 cmets 或任何其他文本,您应该检查前一个节点的类型为 XElement 而不是注释 (XComent) 或文本 (XText),然后在这种情况下使用ElementsBeforeSelf会很容易

    ScripShare = (x.Element(ns + "Name") != null 
                         ? (string)x.Element(ns + "Name")
                         : (x.NodesBeforeSelf().Last(e => e.NodeType == XmlNodeType.Element) != null
                                  ? ((XElement)x.NodesBeforeSelf().Last(e => e.NodeType == XmlNodeType.Element)).Element(ns + "Name").Value
                                  : string.Empty)),
    

    【讨论】:

    • 'System.Xml.Linq.XNode' 不包含'Element' 的定义,并且没有扩展方法'Element' 接受'System.Xml.Linq.XNode' 类型的第一个参数可能是找到
    • 对不起,我忘记将 PreviousNode 转换为 XElement,我已经更新了答案
    • 无法将 System.Xml.Linq.XText 类型的对象转换为 System.Xml.Linq.XElement 类型
    • 我现在正在测试它,它可以工作,你确定不是别的吗?为什么 PreviousNode 是 XText?它应该是 XNode 类型。
    • 我猜你正在用不同的 xml 测试它。似乎您在任何 元素之前都有一个文本。这是一个有效的场景吗?
    【解决方案2】:

    试试这个:

        private static string ns = "{http://www.website.co.za/namespace}";
        private static string getName(XElement x) {
            if (x == null)
                throw new InvalidOperationException("First elements has no Name");
            var val = (string) x.Element(ns + "Name");
            return val ?? getName((XElement)x.PreviousNode);
        }
    
        static void Main(string[] args)
        {
            string xml = @"<ScrpHld xmlns=""http://www.website.co.za/namespace"">
            <Scrp>
                <Name>Company Name Number 1</Name>
                <SecCde>1366J</SecCde>
                <Tran>SOLD</Tran>
                <OpBal>0</OpBal>
                <Move>-2000</Move>
            </Scrp>
            <Scrp>
                <Tran>ELECTRONIC SETTLEMENT</Tran>
                <Move>2000</Move>
                <ClPrce>25045.00</ClPrce>
                <ClBal>0</ClBal>
            </Scrp>
            <Scrp>
                <Name>Company Name Number 2</Name>
                <SecCde>1313J</SecCde>
                <Tran>SOLD</Tran>
                <OpBal>10000</OpBal>
                <Move>-90500</Move>
            </Scrp>
            <Scrp>
                <Tran>ELECTRONIC SETTLEMENT</Tran>
                <Move>80500</Move>
                <ClPrce>3392.00</ClPrce>
                <ClBal>0</ClBal>
            </Scrp> 
            </ScrpHld>";
            XDocument doc = XDocument.Parse(xml);
            var result = doc.Descendants(ns + "Scrp").Select(x => new
                {
                    Name = getName(x)
                });
            foreach (var item in result)
            {
                Console.WriteLine(item.Name);
            }
    

    此方法使用递归方法,因此即使两个连续元素没有名称,它们也会使用前一个元素的名称。

    【讨论】:

      【解决方案3】:

      您可以声明用于在选择方法范围之外捕获当前名称的变量:

      string name = "";
      scripData = ScrpHldNode.Descendants(ns + "Scrp")
                             .Select(scrp => {
                                  var currentName = (string)scrp.Element(ns + "Name");
                                  if (!String.IsNullOrEmpty(currentName))
                                      name = currentName;
      
                                  return new ScripTbl {
                                      ScripShare = name
                                      // ...
                                  };
                             });
      

      没有递归,没有遍历先前元素的 xml 树。


      如果您只需要检查缺少的名称元素,那么代码可以缩短为

      string name = "";
      var scripData = from scrp in ScrpHldNode.Descendants(ns + "Scrp")
                      let currentName = (string)scrp.Element(ns + "Name") ?? name
                      select new ScripTbl {
                           ScripShare = (name = currentName)
                           // ...
                      };
      

      它更短(由于在 ScripShare 分配期间使用空合并运算符和棘手的名称分配),但我会使用更长更明显的代码。

      【讨论】:

        猜你喜欢
        • 2019-02-18
        • 2021-11-04
        • 1970-01-01
        • 2015-08-22
        • 1970-01-01
        • 1970-01-01
        • 2011-05-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多