如何对同名的XML节点进行分组?

时间:2012-07-26 02:57:00

标签: c# xml xmlnode

我试图弄清楚如何对具有相同名称但不同值的XML节点进行分组。

我正在使用的Web服务返回一个如下所示的XmlElement:

<Items>
    <Item>
        <Name name="Name">Item 1</Name>
        <Description name="Description">
            Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        </Description>
        <AssociatedItems name="Associated Items">Item 2</AssociatedItems>
        <AssociatedItems name="Associated Items">Item 3</AssociatedItems>
        <AssociatedItems name="Associated Items">Item 4</AssociatedItems>
        <AssociatedItems name="Associated Items">Item 5</AssociatedItems>
    </Item>
</Items>

我正在将每个节点转换为HTML标记

protected void lnkItem_Click(object sender, EventArgs e)
{
    LinkButton link = (LinkButton)sender;
    GridViewRow row = (GridViewRow)link.Parent.Parent;

    string id = gv.DataKeys[row.RowIndex]["id"].ToString();

    PublicApiAsmxServiceSoapClient service = new PublicApiAsmxServiceSoapClient("PublicApiAsmxServiceSoap", WEB_SERVICE_URL);
    XmlElement xml = service.ItemGetAsXml(id);
    XElement nodes = XElement.Parse(xml.InnerXml);

    foreach (var node in nodes.Elements())
    {
        InsertHTML(node);
    }
}

private void InsertHTML(XElement node)
{
    if (node.Value == string.Empty)
        return;

    pnl.ContentTemplateContainer.Controls.Add(new LiteralControl(String.Format("<h3>{0}</h3>", node.Attribute("name").Value)));
    pnl.ContentTemplateContainer.Controls.Add(new LiteralControl(String.Format("<p>{0}</p>", node.Value)));
}

现在使用我的代码,HTML输出将是:

<h3>Name</h3>
<p>Item 1</p>
<h3>Description</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
<h3>Associated Items</h3>
<p>Item 2</p>
<h3>Associated Items</h3>
<p>Item 3</p>
<h3>Associated Items</h3>
<p>Item 4</p>
<h3>Associated Items</h3>
<p>Item 5</p>

有没有办法将相同的节点组合为无序列表?这样的事情,也许是:

<h3>Name</h3>
<p>Item 1</p>
<h3>Description</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
<h3>Associated Items</h3>
<ul>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>Item 4</li>
    <li>Item 5</li>
</ul>

谢谢! (请你好。)

PS 重复的子节点不仅限于AssociatedTerms。可能有更多同名的子节点重复。

3 个答案:

答案 0 :(得分:1)

使用生成器根据分组生成元素是最简单的,因为生成的内容取决于有多少重复项。

据我了解,您按元素名称和name属性的值进行分组。然后根据组中是否有多个元素生成h3后跟元素。如果有单个元素,则p;如果有多个元素,则ul

IEnumerable<XElement> GroupedElements(XElement root)
{
    var groupedItems =
        from element in root.Elements()
        group element
        by new
        {
            Element = element.Name,
            Name = (string)element.Attribute("name"),
        };
    foreach (var g in groupedItems)
    {
        yield return new XElement("h3", g.Key.Name);
        var isMultiple = g.Skip(1).Any();
        if (isMultiple)
            yield return new XElement("ul",
                from item in g
                select new XElement("li", item.Value.Trim())
            );
        else
            yield return new XElement("p", g.Single().Value.Trim());
    }
}
var xmlStr = @"<Items>
    <Item>
        <Name name=""Name"">Item 1</Name>
        <Description name=""Description"">
            Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        </Description>
        <AssociatedItems name=""Associated Items"">Item 2</AssociatedItems>
        <AssociatedItems name=""Associated Items"">Item 3</AssociatedItems>
        <AssociatedItems name=""Associated Items"">Item 4</AssociatedItems>
        <AssociatedItems name=""Associated Items"">Item 5</AssociatedItems>
    </Item>
</Items>";
var doc = XDocument.Parse(xmlStr);
var transformed = new XElement("div", 
    from item in doc.XPathSelectElements("/Items/Item")
    select GroupedElements(item)
);

产生以下内容:

<div>
  <h3>Name</h3>
  <p>Item 1</p>
  <h3>Description</h3>
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
  <h3>Associated Items</h3>
  <ul>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>Item 4</li>
    <li>Item 5</li>
  </ul>
</div>

答案 1 :(得分:0)

我不确定它是否是最好的解决方案,但你可以尝试这样的事情

       List<string> items = new List<string>();
          XmlReader rdr = XmlReader.Create(new System.IO.StringReader(xml));
          while (rdr.Read())
          {
            if (rdr.NodeType == XmlNodeType.Element)
            {
                InsertHTML(rdr.LocalName.ToString(), rdr.Value.ToString());
            }
          }
        private void InsertHTML(string name, string value)
        {
            if (value == string.Empty)
                return;
            if(name=="AssociatedItems")
             {

               items.add(value);
             }
             else
             {
            pnl.ContentTemplateContainer.Controls.Add(new LiteralControl(String.Format("<h3>{0}</h3>", name)));
            pnl.ContentTemplateContainer.Controls.Add(new LiteralControl(String.Format("<p>{0}</p>", value)));
             }
        }
           pnl.ContentTemplateContainer.Controls.Add(new LiteralControl(String.Format("<h3>{0}</h3>", "Associated Items")));
           pnl.ContentTemplateContainer.Controls.Add(new LiteralControl(String.Format("<ul>")));
        foreach(var item in items)
        {

          pnl.ContentTemplateContainer.Controls.Add(new LiteralControl(String.Format("<li>{0}</li>", item)));

        }
         pnl.ContentTemplateContainer.Controls.Add(new LiteralControl(String.Format("</ul>")));

答案 2 :(得分:0)

如果您想使用XSLT,可以使用:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" indent="yes"/>

  <xsl:key name="grouping" match="Item/*" use="@name"/>

  <xsl:template match="Item">
    <xsl:apply-templates
         select="*[generate-id(.)=generate-id(key('grouping', @name)[1])]"/>
  </xsl:template>

  <xsl:template match="Item/*">
    <h3><xsl:value-of select="@name"/></h3>
    <xsl:choose>
      <xsl:when test="count(key('grouping', @name))=1">
        <p><xsl:value-of select="normalize-space(.)"/></p>
      </xsl:when>
      <xsl:otherwise>
        <li>
          <xsl:for-each select="key('grouping', @name)">
            <ul><xsl:value-of select="normalize-space(.)"/></ul>
          </xsl:for-each>
        </li>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>