简体   繁体   中英

C# LINQ to XML - How can I filter nodes based on a child element?

Given the following XML (partial for brevity), I am attempting to only create a new object if the VALUE element for a CELL element where name is C006 is not a given value.

<alv:AREAS>
    <alv:TABLE>
        <alv:COLUMNS>
        <alv:ROWS>
            <ROWNUMBER>9 </ROWNUMBER>
                <ROW type="D" index="0 ">
                <ROW type="D" index="8 ">
                    <CELL name="C001" visible="X" imagefirst="" imageid="0001"/>
                    <CELL name="C002" visible="X">
                        <VALUE>A7F30024579</VALUE>
                    </CELL>
                    <CELL name="C003" visible="X">
                        <VALUE>Xfrmr 40VA,120-24V,single hub,Class II U</VALUE>
                    </CELL>
                    <CELL name="C004" visible="X">
                        <VALUE decimals="0">3</VALUE>
                    </CELL>
                    <CELL name="C005" visible="X">
                        <VALUE>PTO</VALUE>
                    </CELL>
                    <CELL name="C006" visible="X">
                        <VALUE>BANC</VALUE>
                    </CELL>
                </ROW>
        </ROWS>
    </COLUMNS>
</TABLE>

I thought the following might give me what I am looking for but I think I am trying to apply the filter one level too high (focus on the select for CELL). Without the filter, my list is created as expected. With the filter in place, there are no elements to further select.

var rows = xml.Descendants(ns + "ROWS")
            .SelectMany(row => row.Elements("ROW")).Where(r => r.Attribute("type").Value == "D")
            .Select(c => c.Elements("CELL")).Where(f => f.Attribute("name").Value == "C006").Select(v => v.Element("VALUE").Value != "LEIS"))
            .Select(d => new SAPDevice
            {
                MaterialNumber = d.Where(cell => cell.Attribute("name").Value == "C002").Select(val => val.Element("VALUE").Value).First(),
                PartNumber = d.Where(cell => cell.Attribute("name").Value == "C011").Select(val => val.Element("VALUE").Value).First(),
                Quantity = System.Convert.ToInt32(d.Where(cell => cell.Attribute("name").Value == "C004").Select(val => val.Element("VALUE").Value).First()),
                Price = System.Convert.ToDecimal(d.Where(cell => cell.Attribute("name").Value == "C008").Select(val => val.Element("VALUE").Value).First()),
                Description = d.Where(cell => cell.Attribute("name").Value == "C003").Select(val => val.Element("VALUE").Value).First()//,
                //MaterialType = d.Where(cell => cell.Attribute("name").Value == "C006").Select(val => val.Element("VALUE").Value).First()
            }).ToList();

I think the '.Where' needs to be applied to d but I am unsure how. Thanks for taking a look.

edit 7/19: The following query returns all rows regardless of the filter value:

var rows = xml.Descendants(ns + "ROWS")
            .SelectMany(row => row.Elements("ROW")).Where(r => r.Attribute("type").Value == "D")
            .Select(c => c.Elements("CELL")).Where(f => !f.Elements("CELL").Any(f1 => ((string)f1.Attribute("name") == "C006") && ((string)f1.Element("VALUE") == "BANC")))
            .Select(d => new SAPDevice
            {
                MaterialNumber = d.Where(cell => cell.Attribute("name").Value == "C002").Select(val => val.Element("VALUE").Value).First(),
                PartNumber = d.Where(cell => cell.Attribute("name").Value == "C011").Select(val => val.Element("VALUE").Value).First(),
                Quantity = System.Convert.ToInt32(d.Where(cell => cell.Attribute("name").Value == "C004").Select(val => val.Element("VALUE").Value).First()),
                Price = System.Convert.ToDecimal(d.Where(cell => cell.Attribute("name").Value == "C008").Select(val => val.Element("VALUE").Value).First()),
                Description = d.Where(cell => cell.Attribute("name").Value == "C003").Select(val => val.Element("VALUE").Value).First()//,
                //MaterialType = d.Where(cell => cell.Attribute("name").Value == "C006").Select(val => val.Element("VALUE").Value).First()
            }).ToList();

Try following :

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

namespace ConsoleApplication120
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XDocument xml = XDocument.Load(FILENAME);
            XElement root = xml.Root;
            XNamespace ns = root.GetNamespaceOfPrefix("alv");

            var results = xml.Descendants(ns + "ROWS").SelectMany(rows =>
               rows.Descendants("ROW").Where(r => r.Attribute("type").Value == "D")
               .Where(f => !f.Elements("CELL").Any(f1 => ((string)f1.Attribute("name") == "C006") && ((string)f1.Element("VALUE") == "LEIS")))
               .Select(d => new SAPDevice()
               {
                   MaterialNumber = (string)d.Elements("CELL").Where(cell => (string)cell.Attribute("name") == "C002").Select(val => val.Element("VALUE")).FirstOrDefault(),
                   PartNumber = d.Elements("CELL").Where(cell => (string)cell.Attribute("name") == "C011").Select(val => (string)val.Element("VALUE")).FirstOrDefault(),
                   Quantity = (int?)d.Elements("CELL").Where(cell => (string)cell.Attribute("name") == "C004").Select(val => val.Element("VALUE")).FirstOrDefault(),
                   Price = (decimal?)d.Elements("CELL").Where(cell => (string)cell.Attribute("name") == "C008").Select(val => val.Element("VALUE")).FirstOrDefault(),
                   Description = (string)d.Elements("CELL").Where(cell => (string)cell.Attribute("name") == "C003").Select(val => val.Element("VALUE")).FirstOrDefault(),
               //     //MaterialType = d.Elements("CELL").Where(cell => cell.Attribute("name").Value == "C006").Select(val => val.Element("VALUE").Value).First()
               })).ToList();
        }

    }
    public class SAPDevice
    {
        public object test { get; set; }
        public string MaterialNumber { get; set; }
        public string PartNumber { get; set; }
        public int? Quantity { get; set; }
        public decimal? Price { get; set; }
        public string Description { get; set; }
    }


}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM