xslt разбирает XML-строку в переменную и использует Xpath
Мой (упрощенный) входной XML-файл содержит следующее:
<?xml version="1.0" encoding="UTF-8"?>
<main>
<DATA_RECORD>
<MESSAGE><pd> <cdhead version="13"/> </pd></MESSAGE>
</DATA_RECORD>
</main>
Значение элемента MESSAGE является экранированным символом экземпляром XML. Он представляет собой следующий XML:
<pd>
<cdhead version="13"/>
</pd>
Я хотел бы применить преобразование xsl к входному XML и каким-то образом разобрать содержимое MESSAGE в переменную и использовать выражения Xpath для доступа к ее деталям.
Я попытался добавить функцию javascript, как показано ниже, но объект, возвращаемый сценарием, по-видимому, имеет неправильный подкласс DOM (см. Результат ниже). Для полноты я добавил дополнительную функцию, которая возвращает содержимое DOM в виде строки.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ms="urn:schemas-microsoft-com:xslt"
xmlns:my="http://example.com/my"
exclude-result-prefixes="ms my">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<ms:script language="JScript" implements-prefix="my">
<![CDATA[
function parseToDOM (input) {
var doc = new ActiveXObject('Msxml2.DOMDocument.6.0');
doc.loadXML (input);
return doc.documentElement;
};
function parseToXMLString (input) {
var doc = new ActiveXObject('Msxml2.DOMDocument.6.0');
doc.loadXML (input);
return doc.documentElement.xml;
};
]]>
</ms:script>
<xsl:template match="/">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match="DATA_RECORD">
<xsl:variable name="DOM"><xsl:copy-of select="my:parseToDOM (MESSAGE)"/></xsl:variable>
<xsl:variable name="XML"><xsl:copy-of select="my:parseToXMLString (MESSAGE)"/></xsl:variable>
<msg1><xsl:value-of select="$XML"/></msg1>
<msg2><xsl:value-of select="$XML" disable-output-escaping="yes"/></msg2>
<dom><xsl:copy-of select="$DOM"/></dom>
<version><xsl:value-of select="$DOM/pd/cdhead/@version"/></version>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
Результат:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<msg1><pd>
<cdhead version="13"/>
</pd></msg1>
<msg2><pd>
<cdhead version="13"/>
</pd></msg2>
<dom/>
<version></version>
</root>
Как сделать так, чтобы функция Jscript возвращала результат, позволяющий использовать Xpath?
Кстати, есть ли какая-то функция XSLT 1.0, которая позволяет анализировать экранированную XML-строку для результата, который позволяет использовать Xpath?
Сложение
Я пробовал некоторые варианты и приблизился к решению. Во-первых, Altova XMLSpy позволяет выбрать процессор xsl, и вышеизложенное получилось при использовании встроенного. Конечно, мне нужен MSXML 6.0, и при его выборе возникли ошибки, так как вместо этого мне пришлось анализировать input.text. Но мне удалось использовать в результате выражения Xpath только после выполнения дополнительных действий в javascript. Выяснилось, что в то время как <
и тому подобное разбираются в <
и так далее, этого недостаточно для достижения правильного результата DOM. Поэтому я прибегнул к удалению входной строки первым.
Но я столкнулся с еще одной загвоздкой: где ниже работает нормально, это не так, когда я использую input.text
вместо буквального ниже.
Смотрите ниже xsl t.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ms="urn:schemas-microsoft-com:xslt"
xmlns:my="http://example.com/my"
exclude-result-prefixes="ms my">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<ms:script language="JScript" implements-prefix="my">
<![CDATA[
function parseToDOM (input) {
var doc = new ActiveXObject('Msxml2.DOMDocument.6.0');
doc.loadXML (unescapeXML ('<pd> <cdhead version="13"/> </pd>'));
//doc.loadXML (unescapeXML (input.text));
return doc;
};
function unescapeXML (str) {
var ostr = str;
ostr = ostr.replace (/"/g, '"');
ostr = ostr.replace (/</g, '<');
ostr = ostr.replace (/=/g, '=');
ostr = ostr.replace (/>/g, '>');
return ostr;
};
]]>
</ms:script>
<xsl:template match="/">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match="DATA_RECORD">
<xsl:variable name="msg" select="my:parseToDOM (MESSAGE)"/>
<tst><xsl:value-of select="$msg/pd/cdhead/@version"/></tst>
</xsl:template>
</xsl:stylesheet>
Теперь результаты в
<?xml version="1.0" encoding="UTF-8"?>
<root>
<tst>13</tst>
</root>
Что именно то, что я хочу.
Но, как отмечалось выше, когда я комментирую разбор литерала и вместо него использую ввод, вот так:
//doc.loadXML (unescapeXML ('<pd> <cdhead version="13"/> </pd>'));
doc.loadXML (unescapeXML (input.text));
Я получаю следующую ошибку (в Altova XML Spy с MSXML 6.0 в качестве парсера xsl t):
XSL transformation failed due to following error:
Microsoft JScript runtime error
'undefined' is null or not an object
line = 10, col = 3 (line is offset from the start of the script block).
Error returned from property or method call.
Который указывает на первый оператор замены javascript.
А также, IE9 не может правильно обработать следующее:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="test.xslt"?>
<main>
<DATA_RECORD>
<MESSAGE><pd> <cdhead version="13"/> </pd></MESSAGE>
</DATA_RECORD>
</main>
Когда я открываю этот файл в IE9 (где test.xslt - это версия преобразования, в которой ввод игнорируется и вместо него обрабатывается литерал, а значит, тот, который в XML Spy работает нормально), я получаю ошибку обработки:
XML5001: Applying Integrated XSLT Handling.
XSLT8690: XSLT processing failed.
Почему все это и как я могу это исправить?
1 ответ
Начиная с ДОПОЛНЕНИЯ выше, я нашел решение, немного подстроив его.
Чтобы избежать необходимости делать input.text
и использовать простой input
вместо этого xsl должен содержать преобразование элемента в строку путем применения строковой функции xslt (я думал, что это уже была строка, но, очевидно, это не так). Кроме того, теперь больше не было необходимости применять операторы замены.
таким образом
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ms="urn:schemas-microsoft-com:xslt"
xmlns:my="http://example.com/my"
exclude-result-prefixes="ms my">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<ms:script language="JScript" implements-prefix="my">
<![CDATA[
function parseToDOM (input) {
var doc = new ActiveXObject('Msxml2.DOMDocument.6.0');
doc.loadXML (input);
return doc;
};
]]>
</ms:script>
<xsl:template match="/">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match="DATA_RECORD">
<xsl:variable name="msg" select="my:parseToDOM (string(MESSAGE))"/>
<tst><xsl:value-of select="$msg/pd/cdhead/@version"/></tst>
</xsl:template>
</xsl:stylesheet>
работает: при нанесении на
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="test.xslt"?>
<main>
<DATA_RECORD>
<MESSAGE><pd> <cdhead version="13"/> </pd></MESSAGE>
</DATA_RECORD>
</main>
результат
<?xml version="1.0" encoding="UTF-8"?>
<root>
<tst>13</tst>
</root>
К сожалению, IE9 все еще не может загрузить XML с указанным XSLT; и я понял почему.
Мне пришлось поставить галочку в пункте "Свойства обозревателя" / "Дополнительно" / "Безопасность" / "Разрешить запуск активного содержимого в файлах на моем компьютере", а также перезапустить IE. Это заставляет IE9 правильно обрабатывать файл. Конечно, результат отсутствия html означает, что результат можно просмотреть только на вкладке F12/Script, но это был всего лишь пример, и я включу его в xslt, который генерирует правильный html.