需要有关Linq XML条件分组查询的帮助

时间:2010-05-04 18:43:17

标签: c# xml linq

我有以下xml片段:

 <BANNER ID="Banner 2" ROW_WIDTH="200">
   <BANNER_TEXTS ID="BANNER_TEXTS">
    <BANNER_TEXT UNDERLINE="false" SPAN_COL="1" WIDTHT="78px"></BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="3" WIDTHT="234px">Years In Practice</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="3" WIDTHT="234px">Internet Usage</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="4" WIDTHT="312px">Sales  Reps Seen  / Week</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="3" WIDTHT="234px">Prescription Volume</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="3" WIDTHT="222px">Patient Load</BANNER_TEXT>
   </BANNER_TEXTS>
   <BANNER_TEXTS ID="COLUMN_TEXTS">
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="1" WIDTHT="78px">Total</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="1" WIDTHT="78px">&#60; 11 years</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="1" WIDTHT="78px">11-20 years</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="1" WIDTHT="78px">21-30 years</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="1" WIDTHT="78px">Light 1-5 hrs</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="1" WIDTHT="78px">Medium 6-10 hrs</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="1" WIDTHT="78px">Heavy &#62;10 hrs</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="1" WIDTHT="78px">0</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="1" WIDTHT="78px">1-2</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="1" WIDTHT="78px">3-5</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="1" WIDTHT="78px">&#62;5</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="1" WIDTHT="78px">1-100</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="1" WIDTHT="78px">101-150</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="1" WIDTHT="78px">&#62;150</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="1" WIDTHT="74px">1-100</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="1" WIDTHT="74px">101-200</BANNER_TEXT>
    <BANNER_TEXT UNDERLINE="true" SPAN_COL="1" WIDTHT="74px">&#62;200</BANNER_TEXT>
   </BANNER_TEXTS>
   <BANNER_TEXTS ID="COLUMN_TEXTS">
    <COLUMN_TEXT UNDERLINE="false" SPAN_COL="1">(A)</COLUMN_TEXT>
    <COLUMN_TEXT UNDERLINE="false" SPAN_COL="1">(B)</COLUMN_TEXT>
    <COLUMN_TEXT UNDERLINE="false" SPAN_COL="1">(C)</COLUMN_TEXT>
    <COLUMN_TEXT UNDERLINE="false" SPAN_COL="1">(D)</COLUMN_TEXT>
    <COLUMN_TEXT UNDERLINE="false" SPAN_COL="1">(E)</COLUMN_TEXT>
    <COLUMN_TEXT UNDERLINE="false" SPAN_COL="1">(F)</COLUMN_TEXT>
    <COLUMN_TEXT UNDERLINE="false" SPAN_COL="1">(G)</COLUMN_TEXT>
    <COLUMN_TEXT UNDERLINE="false" SPAN_COL="1">(H)</COLUMN_TEXT>
    <COLUMN_TEXT UNDERLINE="false" SPAN_COL="1">(I)</COLUMN_TEXT>
    <COLUMN_TEXT UNDERLINE="false" SPAN_COL="1">(J)</COLUMN_TEXT>
    <COLUMN_TEXT UNDERLINE="false" SPAN_COL="1">(K)</COLUMN_TEXT>
    <COLUMN_TEXT UNDERLINE="false" SPAN_COL="1">(L)</COLUMN_TEXT>
    <COLUMN_TEXT UNDERLINE="false" SPAN_COL="1">(M)</COLUMN_TEXT>
    <COLUMN_TEXT UNDERLINE="false" SPAN_COL="1">(N)</COLUMN_TEXT>
    <COLUMN_TEXT UNDERLINE="false" SPAN_COL="1">(O)</COLUMN_TEXT>
    <COLUMN_TEXT UNDERLINE="false" SPAN_COL="1">(P)</COLUMN_TEXT>
    <COLUMN_TEXT UNDERLINE="false" SPAN_COL="1">(Q)</COLUMN_TEXT>
   </BANNER_TEXTS>
  </BANNER>

我想使用第一个序列'BANNER_TEXT'作为键将第二个序列中的所有'BANNER_TEXT'分组(仅包括字符串不为空或空的元素)。第一个'BANNER_TEXT'序列中的span_col属性指示第二个序列中按位置的哪些元素是相关的。

一个例子:'实践中的年数'将是第一个键,该元素的属性SPAN_COL = 3表示它将包含'&lt; 11年','11 - 20年','21 - 30年'(第一组string.empty =&gt;总计将被跳过)。

我能够想出这个:

   IEnumerable<XElement> groupCats = child.Descendants("BANNER_TEXTS").ElementAt(0).Descendants("BANNER_TEXT");

    var totals =
            from s in groupCats
            let span = int.Parse(s.Attribute("SPAN_COL").Value)

            group s by s.Value into grouped
            select new
            {
                GroupCategory = grouped.Key,
                Categories = child.Descendants("BANNER_TEXTS").ElementAt(1).Descendants("BANNER_TEXT").Skip(1).Take(1)
    };

到目前为止,我想跳过跨度的总和,并考虑跨度。我不能像现在这样将'span'变量放入查询中。

4 个答案:

答案 0 :(得分:0)

考虑计算相关的(位置),如下例所示。

public class TopTitle
{
  public int Span {get;set;}
  public string Value {get;set;}
  public int Position {get;set;}
}

public class SubTitle
{
  public int Span {get;set;}
  public string Value {get;set;}
  public int Position {get;set;}
}

//
List<Title> Titles = GetTitles();
List<SubTitle> SubTitles = GetSubTitles();
int i = 0;
Titles.ForEach(t =>
{
  t.Position = i;
  i += t.Span;
}
i = 0;
SubTitles.ForEach(st =>
{
  st.Position = i;
  i += st.Span;
}

var query =
  from t in Titles
  let sts =
    from st in SubTitles
    where t.Position <= st.Position
      && st.Position < (t.Position + t.Span)
  select st
  select new {Title = t, SubTitles = sts.ToList()};

答案 1 :(得分:0)

也许您可以通过扩展标准选择来利用以下内容:

public class Grouping
{
    public string Title { get; set; }
    public string Criteria { get; set; }
}


XmlDocument xDoc = new XmlDocument();

var First_Sequence = (from b in XElement.Load("Banners.xml").Elements("BANNER_TEXTS").First().Elements("BANNER_TEXT")
                              where b.Value != ""
                              select b);

var Second_Sequence = (from b in XElement.Load("Banners.xml").Elements("BANNER_TEXTS").Skip(1).First().Elements("BANNER_TEXT")
                               where b.Value != "Total"
                               select b).ToList();

List<Grouping> groups = new List<Grouping>();

int i = 0;
foreach (var item in First_Sequence)
{
    groups.Add(new Grouping { Title = item.Value, Criteria = (Second_Sequence.Skip(i).First().Value).ToString() });
    i++;
}

答案 2 :(得分:0)

这应该迭代每个序列(标题序列和值序列)一次:

static IEnumerable<IGrouping<TFirst, TSecond>> Chunk<TFirst, TSecond>(
    IEnumerable<TFirst> source, 
    IEnumerable<TSecond> toChunk, 
    Func<TFirst, int> chunkSizeSelector)
{
    //error checking here
    using (var chunkItems = toChunk.GetEnumerator())
    {
        foreach (var key in source)
        {
            List<TSecond> items = new List<TSecond>();
            for (int itemsRemaining = chunkSizeSelector(key); itemsRemaining > 0; itemsRemaining--)
            {
                if (!chunkItems.MoveNext())
                    throw new ArgumentException("There are not enough items in toChunk to satisfy source.");
                items.Add(chunkItems.Current);
            }
            yield return new ChunkGrouping<TFirst, TSecond>(key, items);
        }
    }
}

internal class ChunkGrouping<TKey, TElement> : IGrouping<TKey, TElement>
{
    public ChunkGrouping(TKey key, IEnumerable<TElement> elements)
    {
        if (elements == null) throw new ArgumentNullException("elements");
        _key = key;
        _elements = elements;
    }

    private readonly TKey _key;
    private readonly IEnumerable<TElement> _elements;

    public TKey Key { get { return _key; } }

    IEnumerator<TElement> IEnumerable<TElement>.GetEnumerator()
    {
        return _elements.GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return _elements.GetEnumerator();
    }
}

然后您可以将其用作:

foreach (var group in Chunk(child.Elements("BANNER_TEXTS").ElementAt(0).Elements(),
                            child.Elements("BANNER_TEXTS").ElementAt(1).Elements(),
                            xe => (int)xe.Attribute("SPAN_COL")))
{
    //do stuff with the elements
}

答案 3 :(得分:0)

我最终使用了这个:

foreach (XElement child in e.Elements("BANNER"))
            {

                IEnumerable<XElement> groups = child.Descendants("BANNER_TEXTS").ElementAt(0).Descendants("BANNER_TEXT");

                var groupCats =
                from s in groups
                group s by s.Value into grouped
                select new
                {
                    GroupCategory = grouped.Key,
                    Categories = GetCategories(grouped.Key, child)
                };
            }

 private IEnumerable<string> GetCategories(string key, XElement parent)
    {
        int span = parent.Descendants("BANNER_TEXTS").ElementAt(0).Descendants("BANNER_TEXT").Where(x => x.Value == key).Select(x => int.Parse(x.Attribute("SPAN_COL").Value)).FirstOrDefault();
        IEnumerable<int> set = Series(key,parent.Descendants("BANNER_TEXTS").ElementAt(0).Descendants("BANNER_TEXT"));
        int sum = set.Sum();        
        return parent.Descendants("BANNER_TEXTS").ElementAt(1).Descendants("BANNER_TEXT").Skip(sum).Take(span).Select(x => x.Value);

    }

    private static IEnumerable<int> Series(string key, IEnumerable<XElement> elements)
    {

        foreach (XElement item in elements)
        {
            if (item.Value != key)
            {
                yield return int.Parse(item.Attribute("SPAN_COL").Value);

            }
            else
            {
                break;
            }

        }

    }