我试图弄清楚如何对具有相同名称但不同值的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。可能有更多同名的子节点重复。
答案 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>