.NET Extension Objects с XSLT - как перебирать коллекцию?
Помогите мне, Stackoverflow!
У меня есть простое консольное приложение .NET 3.5, которое считывает некоторые данные и отправляет электронные письма. Я представляю формат электронной почты в таблице стилей XSLT, чтобы мы могли легко изменить формулировку электронной почты без необходимости перекомпилировать приложение.
Мы используем Extension Objects для передачи данных в XSLT при применении преобразования:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
xmlns:EmailNotification="ext:EmailNotification">
- таким образом, мы можем иметь такие утверждения, как:
<p>
Dear <xsl:value-of select="EmailNotification:get_FullName()" />:
</p>
Вышеописанное работает отлично. Я передаю объект через код, подобный этому (для краткости неактивен код):
// purely an example structure
public struct EmailNotification
{
public string FullName { get; set; }
}
// Somewhere in some method ...
var notification = new Notification("John Smith");
// ...
XsltArgumentList xslArgs = new XsltArgumentList();
xslArgs.AddExtensionObject("ext:EmailNotification", notification);
// ...
// The part where it breaks! (This is where we do the transformation)
xslt.Transform(fakeXMLDocument.CreateNavigator(), xslArgs, XmlWriter.Create(transformedXMLString));
Итак, весь приведенный выше код работает. Тем не менее, я хотел получить немного фантазии (всегда мое падение) и передать коллекцию, чтобы я мог сделать что-то вроде этого:
<p>The following accounts need to be verified:</p>
<xsl:for-each select="EmailNotification:get_SomeCollection()">
<ul>
<li>
<xsl:value-of select="@SomeAttribute" />
</li>
</ul>
<xsl:for-each>
Когда я передаю коллекцию в объекте расширения и пытаюсь преобразовать, я получаю следующую ошибку:
"Extension function parameters or return values which have Clr type 'String[]' are not supported."
или List, или IEnumerable, или все, что я пытаюсь передать.
Итак, мои вопросы:
Я пытаюсь сделать невозможным?
Изменить: после того, как я увидел два ответа ниже, я взял код Криса Хайнса и немного изменил его в соответствии с моими потребностями. Решение выглядит следующим образом:
// extension object
public struct EmailNotification
{
public List<String> StringsSetElsewhere { private get; set; }
public XPathNavigator StringsNodeSet
{
get
{
XmlDocument xmlDoc = new XmlDocument();
XmlElement root = xmlDoc.CreateElement("Strings");
xmlDoc.AppendChild(root);
foreach (var s in StringsSetElsewhere)
{
XmlElement element = xmlDoc.CreateElement("String");
element.InnerText = s;
root.AppendChild(element);
}
return xmlDoc.CreateNavigator();
}
}
}
И в моем XSLT...
<xsl:for-each select="EmailNotification:get_StringsNodeSet()//String">
<ul>
<li>
<xsl:value-of select="." />
</li>
</ul>
</xsl:for-each>
Работал отлично!
Ответы
Ответ 1
XSLT for-each ожидает, что node настроится на итерацию - вы не можете напрямую передать массив с вашего кода на С#. Вы должны вернуть XPathNodeIterator.
Что-то вроде этого:
public static XPathNodeIterator GetSomeCollection()
{
XmlDocument xmlDoc = new XmlDocument();
string[] stringsToReturn = new string[] { "String1", "String2", "String3" };
XmlElement root = xmlDoc.CreateElement("Strings");
xmlDoc.AppendChild(root);
foreach (string s in stringsToReturn)
{
XmlElement el = xmlDoc.CreateElement("String");
el.InnerText = s;
root.AppendChild(el);
}
XPathNodeIterator xNodeIt = xmlDoc.CreateNavigator().Select(".");
return xNodeIt;
}
Ответ 2
В документации указано:
Параметр должен соответствовать типу W3C. В следующей таблице показаны типы W3C, либо XPath, либо XSLT, и соответствующий класс .NET.
И перечисляет следующие типы:
- Строка
- Boolean
- Номер
- Фрагмент дерева результатов
- Node Установите
- Node
Итак, ответ не в том, что вы не можете... ну, по крайней мере, не напрямую. Но лучший вариант в вашем случае: IMHO, Node или Node Set.