Hi all, and thanks in advance for your help.

I am trying to write a Linq to XML query that will test attribute values in multiple elements and return the possibly multiple qualifying entries from a main element. The XML I'm looking at is basically like this:

<ContentList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="../ContentList.xsd">
  <ContentType Type="Live">
    <Content Type="Live" Id="8475" ChannelorUrl="8475" Deploy="MulticastICC" SystemType="RTP" TotalBitrate="2200" BranchName="Working_Branch">
      <Video Codec="H264" Resolution="480x480" Size="SD" Bitrate="2000" Framerate="29.97i" AspectRatio="4:3" EncodingMethod="CVBR" GOPStructure="IPBB" GOPLength="30" />
      <Pip Codec="H264" Resolution="128x96" Size="PIP" Bitrate="250" Framerate="29.97p" AspectRatio="4:3" EncodingMethod="CVBR" />
      <Audio Codec="Mpeg1L1" Channel="2.0" Bitrate="128" Samplingrate="48" Language="Eng" />
      <AnalogClosedCaption Id="CC1" CCType="608" />
    </Content>
</ContentList>

And the query I've built in C# looks like this:

XDocument contDoc = XDocument.Load("C:\\Configs\\ContentList.xml");
      XDocument profDoc = XDocument.Load("AvProfileRequest.xml");

      var searched =
        from p in profDoc.Descendants("type")
        select p.Value; //This part works
      foreach (string str in searched)
      {
        var contList =
          ((from c in contDoc.Descendants("Content")
           where (string)c.Attribute("Type") == (str)
           (from d in c.Descendants("Video")
              where (string) d.Attribute("Codec") == ("H264")
           select new
           {
             Id = (string)c.Attribute("Id")
           }
           ))).ToList();

What I am trying to do is take a value, e.g. "Live" and get the ContentType element, then query that element for values in its sub elements (Video and/or Pip and/or Audio; etc.) for specific values; then return one of more attribute values from Content. I have the code snippet above; but the build keeps failing with the error "A query body must end with a select clause or a group clause". I've tried several, what I think are logical, placements of the parenthesis; but I keep getting the same error at one location or the other.

Please advise me on what I may be doing wrong.
Thanks!

Dani AI

Generated

Good catch by on the missing select. In your snippet you started a second query (inner from) without finishing the first one, which triggers the “query body must end with a select or group” error. Rather than nesting query expressions, keep a single pipeline and test child elements with Any. That also lets you scope to the matching ContentType first so you are not scanning the whole document.

Here is a compact pattern that returns the Content Ids where the parent ContentType matches str and the Content has specific child attributes. It also shows how to compose a variable number of criteria without adding more from clauses:

// helpers so you can tack on as many rules as you need
Func<XElement,string,string,string,bool> hasAttr =
    (c, child, attr, val) => c.Elements(child).Any(e => (string)e.Attribute(attr) == val);

// build your criteria at runtime
var criteria = new List<Func<XElement,bool>> {
    c => hasAttr(c, "Video", "Codec", "H264"),
    // add more rules as needed, e.g. Pip/Audio/etc.
    // c => hasAttr(c, "Pip", "Size", "PIP"),
    // c => hasAttr(c, "Audio", "Language", "Eng"),
};

var ids =
    contDoc.Root
           .Elements("ContentType")
           .Where(ct => (string)ct.Attribute("Type") == str)
           .Elements("Content")
           .Where(c => criteria.All(rule => rule(c)))   // use Any(...) if you want OR logic
           .Select(c => (string)c.Attribute("Id"))
           .ToList();

Notes:

  • Prefer Elements over Descendants when you know the structure; it is faster and clearer.
  • Casting attributes to string returns null on missing values, avoiding NullReferenceException.
  • If your real XML uses a default namespace (not shown here), create XNamespace ns = "..." and use ns + "Content" for element names, or your queries will return nothing.

Recommended Answers

All 3 Replies

It looks like a parenthesis has blocked out one of the from statements so it is isolated. If it is isolated, it has to have its own select statement.
It either needs another select statement or you need to remove the extra set of parentheses to allow the from statements to work together.

Try something like this

var contList = 
    (from c in contDoc.Descendants("Content")
    where (string)c.Attribute("Type") == str
    select c
    into ctemp
        where (string)ctemp.Attribute("Codec") == "H264"
        select new {
            Id = (string)c.Attribute("Id")
        }).ToList();

Thanks Thines01! The information you provided is exactly what I needed!
Using this, I should be able to build my criteria for a varying number of "from targets"!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.