Кросс-браузер xml довольно печатает альтернативу преобразованию идентичности

63
8

При разработке приложения с одной страницей я генерирую XML-код, который я бы хотел прочитать в браузере.

Во время разработки я обычно работаю над любой последней версией Safari, и все это отлично работает, но когда я хотел показать кому-то определенный прогресс, и они использовали Firefox, сгенерированный XML был в одной строке.

Я выбрал преобразование идентичности для печати на основе исследований, которые я сделал, потому что это казалось самым чистым решением моей проблемы, но теперь это, похоже, не работает на браузерах, основанных на Gecko.

Firefox and Safari side by side

Код, который я использую в Typcript:

private prettifyXml(unformattedDocument:XMLDocument) : XMLDocument {

let identityTransformSheet = '\<' +
'xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">\
<xsl:output omit-xml-declaration="yes" indent="yes"/>\
<xsl:template match="node()|@*">\
<xsl:copy>\
<xsl:apply-templates select="node()|@*"/>\
</xsl:copy>\
</xsl:template>\
</xsl:stylesheet>';

let parser = new DOMParser();
let processor = new XSLTProcessor();
processor.importStylesheet(parser.parseFromString(identityTransformSheet, 'text/xml'));
let result = processor.transformToDocument(unformattedDocument);
return result;
}

И это используется:

    // 'xml' holds a document created by DOMParser and filled with a serialized representation of the model.

let result = (new XMLSerializer()).serializeToString(this.prettifyXml(xml));
this.xmlEquivalent(result);
return result;

Это отлично работает в Safari, но, похоже, ничего не делает в Firefox. Есть ли способ сделать эту функциональность перекрестным браузером или, возможно, лучший способ сделать это?

(Я надеюсь на стандартное решение без добавления другой библиотеки)

спросил(а) 2021-01-25T17:33:18+03:00 4 месяца, 2 недели назад
1
Решение
63

Когда вы попросили альтернативы, сообщение, которое вы связали с обсуждением использования преобразования идентичности, предложенное Димитрием Новачевым, также имеет комментарий, предполагающий использование таблицы стилей визуализации XPath вместо преобразования идентичности. Поэтому, чтобы продемонстрировать, что это возможно, я сделал некоторые адаптации к XSLT и CSS и Javascript, чтобы позволить им использоваться внутри другого документа HTML вместо создания отдельного полного документа HTML, результатом является https://martin-honnen.github.io/js/2017/pretty-print/pretty-print-test1.html, который работает для меня в Windows 10 с текущими версиями Edge, Chrome и Firefox. У меня нет Safari для тестирования. Это, безусловно, не будет работать так, как написано с IE, поскольку я только использовал API XSLTProcessor в Javascript, впервые представленный в Mozilla, и теперь поддерживаемый во всем, кроме IE; если вам нужна поддержка IE, вы должны иметь это, используя специальный код IE для запуска преобразования XSLT.

Вот код HTML:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Testing a proof of concept of pretty-printing of an XML DOM tree as a HTML collapsible table structure</title>
<link rel="stylesheet" type="text/css" href="pretty-print1.css"/>
<script>
var prettyPrinter = new XSLTProcessor();
(function() {
var req = new XMLHttpRequest();
req.open('GET', 'pretty-print1.xsl');
req.onload = function() {
prettyPrinter.importStylesheet(req.responseXML);
};
req.send();
}())

function prettyPrint(doc) {
return prettyPrinter.transformToFragment(doc, document);
}

function prettyPrintCollapseExpandHandler(event) {
try {
var thisNode = event.target;
var par = event.target.parentNode;
if (thisNode.nodeName == 'TD' && thisNode.className == 'expander') {
if (par.parentNode.className == 'expander-closed') {
par.parentNode.className = '';
thisNode.textContent = '-';
}
else {
par.parentNode.className = 'expander-closed';
thisNode.textContent = '+';
}
}
} catch (e) {
}
}
</script>
<script>
document.addEventListener('DOMContentLoaded',
function() {
var req = new XMLHttpRequest();
req.open('GET', 'input1.xml');
req.onload = function() {
document.getElementById('result').appendChild(prettyPrint(req.responseXML));
};
req.send();
},
false
);
</script>
</head>
<body>
<section>
<h1>Testing a proof of concept of pretty-printing of an XML DOM tree as a HTML collapsible table structure</h1>
<section id="result">
<h2>Example result</h2>
</section>
</section>
</body>
</html>

XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<!-- The following is not used because of a bug in Mozilla :( -->
<!--
<xsl:key name="kattPref" match="@*"
use="concat(generate-id(..), '|', substring-before(., ':'))"/>
-->
<xsl:output method="html"/>
<xsl:template match="/">

<div class="pretty-print" onclick="prettyPrintCollapseExpandHandler(event);">
<xsl:apply-templates/>
</div>

</xsl:template>

<xsl:template match="*">
<div class="indent">
<span class="markup"><</span>

<xsl:variable name="class" select="'elemname'"/>

<span class="{$class}">
<xsl:value-of select="name(.)"/>
</span>

<xsl:call-template name="findNamespace"/>

<xsl:apply-templates select="@*"/>
<span class="markup">/></span>
</div>
</xsl:template>

<xsl:template match="*[text()]">

<xsl:variable name="class" select="'elemname'"/>

<div class="indent">
<span class="markup"><</span>
<span class="{$class}">
<xsl:value-of select="name(.)"/>
</span>

<xsl:call-template name="findNamespace"/>

<xsl:apply-templates select="@*"/>
<span class="markup">></span>
<!--<span class="text">
<xsl:value-of select="."/> -->
<xsl:apply-templates/>
<!--</span>-->
<span class="markup"></</span>
<span class="elemname">
<xsl:value-of select="name(.)"/>
</span>
<span class="markup">></span>
</div>
</xsl:template>

<xsl:template match="*[* or processing-instruction() or comment()
or string-length(text()) > 50]" priority="10">

<xsl:variable name="class" select="'elemname'"/>

<table>
<tr>
<td class="expander">
-
<div/>
</td>
<td>
<span class="markup"><</span>
<span class="{$class}">
<xsl:value-of select="name(.)"/>
</span>
<xsl:call-template name="findNamespace"/>
<xsl:apply-templates select="@*"/>
<span class="markup">></span>
<div class="expander-content">
<xsl:apply-templates/>
</div>
<span class="markup"></</span>
<span class="elemname">
<xsl:value-of select="name(.)"/>
</span>
<span class="markup">></span>
</td>
</tr>
</table>
</xsl:template>
<xsl:template match="@*">

<xsl:variable name="vPos" select="position()"/>

<xsl:variable name="vPref" select="substring-before(name(), ':')"/>

<xsl:if test="$vPref
and
not(../@*[position() < $vPos]
[substring-before(name(), ':')
= $vPref]
)">
<xsl:call-template name="findNamespace"/>
</xsl:if>

<!-- The following is not used because of a bug in Mozilla :( -->

<!--
<xsl:if test=
"generate-id()
=
generate-id(key('kattPref',
concat(generate-id(..), '|', substring-before(., ':'))
)[1]
)">
<xsl:call-template name="findNamespace"/>
</xsl:if>
-->
<xsl:variable name="class" select="'attrname'"/>

<xsl:variable name="class2" select="'markup'"/>

<xsl:variable name="class3" select="'attrvalue'"/>

<xsl:text> </xsl:text>
<span class="{$class}">
<xsl:value-of select="name(.)"/>
</span>
<span class="{$class2}">="</span>
<span class="{$class3}">
<!-- <xsl:value-of select="."/> -->
<xsl:call-template name="replaceAmpersands">
<xsl:with-param name="vString" select="string(.)"/>
</xsl:call-template>
</span>
<span class="{$class2}">"</span>
</xsl:template>

<xsl:template match="text()">

<xsl:variable name="class" select="'text'"/>

<span class="{$class}">
<!-- <xsl:value-of select="."/> -->
<xsl:call-template name="replaceAmpersands">
<xsl:with-param name="vString" select="string(.)"/>
</xsl:call-template>
</span>
</xsl:template>

<xsl:template match="processing-instruction()">

<xsl:variable name="class" select="'indent pi'"/>

<div class="{$class}">

<?
<xsl:value-of select="name(.)"/>
<xsl:text> </xsl:text>
<xsl:value-of select="."/>
?>

</div>
</xsl:template>

<xsl:template match="processing-instruction()[string-length(.) > 50]">

<xsl:variable name="class" select="'pi'"/>

<xsl:variable name="class2" select="'indent expander-content'"/>

<table>
<tr>
<td class="expander">
-
<div/>
</td>
<td class="{$class}">

<?
<xsl:value-of select="name(.)"/>
<div class="{$class2}">
<xsl:value-of select="."/>
</div>
<xsl:text>?></xsl:text>
</td>
</tr>
</table>
</xsl:template>

<xsl:template match="comment()">

<xsl:variable name="class" select="'comment indent'"/>

<div class="{$class}">
<!--
<xsl:value-of select="."/>
-->
</div>
</xsl:template>

<xsl:template match="comment()[string-length(.) > 50]">

<xsl:variable name="class" select="'comment'"/>

<xsl:variable name="class2" select="'indent expander-content'"/>

<table>
<tr>
<td class="expander">
-
<div/>
</td>
<td class="{$class}">
<!--
<div class="{$class2}">
<xsl:value-of select="."/>
</div>
-->
</td>
</tr>
</table>
</xsl:template>

<xsl:template name="findNamespace">

<xsl:variable name="vName" select="substring-before(name(), ':')"/>
<xsl:variable name="vUri" select="namespace-uri(.)"/>

<xsl:variable name="vAncestNamespace">
<xsl:call-template name="findAncNamespace">
<xsl:with-param name="pName" select="$vName"/>
<xsl:with-param name="pUri" select="$vUri"/>
</xsl:call-template>
</xsl:variable>

<xsl:if test="not(number($vAncestNamespace))">
<xsl:if test="namespace-uri()
or
not(generate-id()
=
generate-id(../@*[name()
=
name(current())]
)
)">
<xsl:if test="parent::* or namespace-uri() or contains(name(), ':')">
<xsl:text> </xsl:text>
<span class="namespace">
<xsl:value-of select="'xmlns'"/>
<xsl:if test="contains(name(), ':')">
<xsl:value-of select="concat(':', $vName)"/>
</xsl:if>
</span>
<span class="markup">="</span>
<span class="namespace">
<xsl:value-of select="namespace-uri()"/>
</span>
<span class="markup">"</span>
</xsl:if>
</xsl:if>
</xsl:if>
</xsl:template>

<xsl:template name="findAncNamespace">
<xsl:param name="pNode" select="."/>
<xsl:param name="pName" select="substring-before(name(), ':')"/>
<xsl:param name="pUri" select="namespace-uri(.)"/>

<xsl:choose>
<xsl:when test="not($pNode/parent::*)
and not($pName) and not($pUri)">1</xsl:when>
<xsl:when test="not($pNode/parent::*)">0</xsl:when>
<xsl:otherwise>
<xsl:variable name="vSamePrefs"
select="number($pName
= substring-before(name($pNode/..), ':')
)"/>

<xsl:variable name="vSameUris"
select="number($pUri = namespace-uri($pNode/..))"/>

<xsl:choose>
<xsl:when test="$vSamePrefs and not($vSameUris)">0</xsl:when>
<xsl:when test="not($vSamePrefs)">
<xsl:call-template name="findAncNamespace">
<xsl:with-param name="pNode" select="$pNode/.."/>
<xsl:with-param name="pName" select="$pName"/>
<xsl:with-param name="pUri" select="$pUri"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>

</xsl:template>

<xsl:template name="replaceAmpersands">
<xsl:param name="vString"/>

<xsl:variable name="vAmp">&</xsl:variable>

<xsl:choose>
<xsl:when test="contains($vString, $vAmp)">
<xsl:value-of select="substring-before($vString, $vAmp)"/>
<xsl:value-of select="concat($vAmp, 'amp;')"/>
<xsl:call-template name="replaceAmpersands">
<xsl:with-param name="vString"
select="substring-after($vString, $vAmp)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$vString"/>
</xsl:otherwise>
</xsl:choose>

</xsl:template>
</xsl:stylesheet>

Возьмите CSS с https://martin-honnen.github.io/js/2017/pretty-print/pretty-print1.css, все это означает, что в любом случае это доказательство концепции, а не как отполированный, готовый код.

В эти дни я бы предпочел предложить разработанное решение, например https://github.com/pgfearo/xmlspectrum, которое может работать в браузере с помощью Saxon-CE или Saxon-JS.

ответил(а) 2021-01-25T17:33:18+03:00 4 месяца, 2 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема