简体   繁体   中英

C#: How can I filter the results of an XML file based on a child element's attribute?

I probably could have worded the title better, but I'm venturing into areas of programming that I've never been to before, so I'm still learning the terminology. But here's what I'm trying to do:

I'm writing a program that can perform structural analyses on bolted joints. However, rather than have the user enter the bolt geometry every single time, I'd like to give them an option to select from a standard list of ASME Unified Thread Standard (UTS) sizes. So I've created a Bolt class and then a UTSBolt subclass. And I'm making an XML file for UTS bolt sizes. And so far, I can de-serialize the XML file, project it to an IEnumerable of my UTSBolts class, let the user select a bolt, and everything is hunky-dory.

But here's my problem...

UTS sizes will specify a bolt's diameter as well as it's thread density for both a coarse-thread (UNC) and a fine-thread (UNF) bolt. As such, I've formatted my XML file like this:

<Bolts_UTS>
  <Bolt>
    <Size>#0</Size>
    <MajorDiameter>0.0600</MajorDiameter>
    <ThreadDensity Series="UNF">80</ThreadDensity>
  </Bolt>
  <Bolt>
    <Size>#1</Size>
    <MajorDiameter>0.0730</MajorDiameter>
    <ThreadDensity Series="UNC">64</ThreadDensity>
    <ThreadDensity Series="UNF">72</ThreadDensity>
  </Bolt>
  <Bolt>
    <Size>#2</Size>
    <MajorDiameter>0.0860</MajorDiameter>
    <ThreadDensity Series="UNC">56</ThreadDensity>
    <ThreadDensity Series="UNF">64</ThreadDensity>
  </Bolt>
  <Bolt>
    <Size>#3</Size>
    <MajorDiameter>0.0990</MajorDiameter>
    <ThreadDensity Series="UNC">48</ThreadDensity>
    <ThreadDensity Series="UNF">56</ThreadDensity>
  </Bolt>
  <Bolt>
    <Size>#4</Size>
    <MajorDiameter>0.1120</MajorDiameter>
    <ThreadDensity Series="UNC">40</ThreadDensity>
    <ThreadDensity Series="UNF">48</ThreadDensity>
  </Bolt>
</Bolts_UTS>

When the user selects a bolt size, I'd like them to be able to select a thread series (UNC/UNF) as well. But I can't seem to figure out how to properly set the filters to read in only ThreadDensity where the Series attribute is "UNF". My program always grabs the first ThreadDensity value, regardless of the attribute.

Can somebody please help me figure out what I'm doing wrong? Here's my code:

static void Main(string[] args)
    {
        string pathCurrent = Directory.GetCurrentDirectory();
        string pathToXML = Path.GetFullPath(Path.Combine(pathCurrent, "Bolts_UTS.xml"));

        XElement boltsUTS = XElement.Load(pathToXML);
        IEnumerable<UTSBolt> boltList =
            from el in boltsUTS.Elements("Bolt")
            where (
                from thread in el.Elements("ThreadDensity")
                where
                    (string)thread.Attribute("Series") == "UNF"
                select thread).Any() &&
                ((string)el.Element("Size") == "#1")
            select new UTSBolt(
                (string)el.Element("Size"),
                (double)el.Element("MajorDiameter"),
                (double)el.Element("ThreadDensity")
            );
        Console.WriteLine("        |    Major    |");
        Console.WriteLine("UN Size\t| Dia. (inch) | Thr. / In.");
        Console.WriteLine("--------|-------------|------------");
        foreach (UTSBolt bolt in boltList)
            Console.WriteLine(bolt);

        Console.ReadLine();
    }

Output:

        |    Major    |
UN Size | Dia. (inch) | Thr. / In.
--------|-------------|------------
#1      |   0.07300   |     64

You're only looking at the Series attribute in this subquery :

 where (
    from thread in el.Elements("ThreadDensity")
    where
        (string)thread.Attribute("Series") == "UNF"
    select thread).Any() &&
    ((string)el.Element("Size") == "#1")

That just checks whether any ThreadDensity element within el has the right series.

The code where you're actually creating the UTSBolt just gets the first ThreadDensity element:

select new UTSBolt(
    (string)el.Element("Size"),
    (double)el.Element("MajorDiameter"),
    (double)el.Element("ThreadDensity")
);

I suspect you want something like:

var boltList =
    from el in boltsUTS.Elements("Bolt")
    let thread = el.Elements("ThreadDensity")
                   .FirstOrDefault(t => (string) t.Attribute("Series") == "UNF")
    let size = (string) el.Element("Size")
    where thread != null && size == "#1"
    select new UTSBolt(size, (double) el.Element("MajorDiameter"), (double) thread);

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