使用多个XSL文件转换XML

16

我有一些XML需要使用多个XSL文件转换为HTML。这些XSL文件通过xsl:import和xsl:include语句相关联,而且所有文件都需要才能完成转换。

我知道XSL有效,因为在预先创建的XML文件中使用<?xml-stylesheet type="text/xsl" href="transform.xsl"?>指令,在浏览器打开该文件可以显示所需的输出结果。问题是,我想在动态生成的XML上复制此功能。

我看到有两种可能的解决方法,但似乎都有限制,我无法解决。

第一种解决方案是使用JavaScript转换XML。据我所知,这将需要使用XSLTProcessor对象加载多个XSL文件,但Chrome(以及其他浏览器)对xsl:import支持不好 - http://code.google.com/p/chromium/issues/detail?id=8441

我也尝试将XML写入iFrame或新窗口,但<?xml-stylesheet type="text/xsl" href="transform.xsl"?>指令在结果窗口中被注释掉了。实际上,任何写入新窗口的内容都是HTML - 我没有找到将XML写入新窗口的方法。

那么,如何使浏览器窗口显示使用一组XSL文件转换的XML文件的结果呢?

更新

因此,这是我对该问题的研究结果。

可能的解决方法:使用emscripten将诸如xsltproc之类的工具编译为JavaScript。我实际上已经做到了 - 参见https://github.com/mcasperson/xsltproc.js

问题: 在Firefox中速度非常慢(在Chrome中需要5秒的操作,在Firefox中需要30秒以上),并且无法在Chrome Web Worker中运行代码 - https://code.google.com/p/chromium/issues/detail?id=252492

可能解决方法: 完全不使用XSL,而是使用CSS样式表显示XML。

问题: 直到浏览器开始实现css attr(attribute name, url)函数,否则没有办法将XML属性中的文件引用视为除字符串外的任何内容,这使得无法显示图像。

可能解决方法: 将所有XSL文件合并成一个样式表

问题: 这在某种程度上是可能的(参见Merge multiple xslt stylesheets),但是xsl:import和xsl:include具有特定的语义,当仅仅替换一个文件的内容以代替xsl:import或xsl:include语句时,这些语义无法传递。对于分成多个文件的大型XSL变换,该解决方案需要大量手动工作。

可能解决方法: 将XML内容写入iframe或新窗口。

问题: 不可能将XML写入新窗口或iframe。这些元素中写入的内容始终被假定为HTML,并插入到HTML->BODY元素中。

可能解决方法: 创建一个服务器端服务,接受XML,然后返回带有XSL样式表指令的XML。该服务URL可以用作iframe或新窗口的src属性。

问题: 该服务必须是GET端点,这意味着要返回的XML必须包含为查询参数,这意味着您最终会遇到URL长度的问题。

可能的解决方法: 使用JavaScript XSL库,例如Saxonica CE

问题: 这种方法可能有效(我没有尝试过),但是Saxonica CE不是开源的(这是我们项目的要求之一)。


1
您可以让XSLTProcessor导入第一个样式表,使用transformToDocument()进行第一次转换,保存结果,加载第二个样式表并再次使用transformToDocument(),提供第一次转换的结果。 - Thomas W
4
为什么你想在浏览器中转换XML?直接在服务器端操作即可。 - Tomalak
4
获取将XML转换为HTML后的实时预览。在服务器上执行此操作需要不断调用服务器端服务,会浪费大量资源。 - Phyxx
我会在服务器上完成这个任务。在浏览器中进行复杂的XSLT转换并支持多种浏览器,我认为这很容易出错。我认为性能不会差。 - MiMo
2
//IE html = xml.transformNode(xsl);//FF, Chrome xsltProcessor = new XSLTProcessor(); xsltProcessor.importStylesheet(xsl); html = xsltProcessor.transformToFragment(xml, document); - Shawn
显示剩余5条评论
3个回答

2
如果您想要一个仅基于浏览器的解决方案,我会这样做:

静态XML

制作一个简单的静态XML,其中只包含对XSL的调用。这是在浏览器中始终打开的XML。此XML文件可以包含属性设置以控制流程,也可以什么都不包含,就像此示例一样。
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="cartoon2html.xsl"?>
<xml/>

动态纯文本XML

使用定义的名称(例如cartoons.xml)以您喜欢的方式生成动态XML。

<?xml version="1.0" encoding="utf-8"?>
<cartoons>
    <cartoon name="Donald Duck" publisher="Walt Disney" />
    <cartoon name="Mickey Mouse" publisher="Walt Disney" />
    <cartoon name="Batman" publisher="DC Comics" />
    <cartoon name="Superman" publisher="DC Comics" />
    <cartoon name="Iron Man" publisher="Marvel Comics" />
    <cartoon name="Spider-Man" publisher="Marvel Comics" />
</cartoons>

使用文档加载的XSLT

在XSLT中使用文档加载来引用生成的动态XML。通过在第一个apply-templates中使用select,所有其他模板将按预期工作。

仔细查看顶部和代码中更深处的变量引用。这就是魔法发生的地方。

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

    <xsl:variable name="cartoons" select="document('cartoons.xml')/cartoons" />

    <xsl:template match="/">
        <html>
            <head>
                <title>Cartoons</title>
                <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
            </head>
            <body>
                <xsl:apply-templates select="$cartoons" />
            </body>
        </html>
    </xsl:template>

    <xsl:template match="cartoons">
        <table>
            <xsl:apply-templates />
        </table>
    </xsl:template>

    <xsl:template match="cartoon">
        <tr>
            <td><xsl:value-of select="@name" /></td>
            <td><xsl:value-of select="@publisher" /></td>
        </tr>
    </xsl:template>

</xsl:stylesheet>

您可以将这三个文件保存到任意目录中,并在Firefox中打开静态XML文件。(Chrome和Safari可能需要通过Web服务器提供文件才能执行转换)。


1
我建议使用jQuery来导入和样式化XML。
类似这样的东西可以让你在调用函数时导入XML(一个与按键、刷新按钮甚至计时器相关联的函数)。
$.ajax({
    type: "GET",
    url: "FILENAME.xml",
    dataType: "xml",
    success: function(xml) {
        $(xml).find('site').each(function(){ //finds parent node
            var id = $(this).attr('id'); //id of parent node
            var title= $(this).find('title').text();  //finds title node within parent node
            var url = $(this).find('url').text(); //finds URL node within parent node
            var description = $(this).find('descr').text(); //etc...
            var img = $(this).find('img').text(); //etc...

            // Creates div with id of parent node (for individual styling)
            $('<div id="link_'+id+'">')
                .addClass('add a div class')
            //sets css of div
                .css({set css})
            // Sets the inner HTML of this XML allocated div to hyperlinked 'title' with 'description' and an 'img'
                .html('<a href="'+url+'">'+title+'</a>'+description+'<img src="'+img+'">')                  
            // Append the newly created element to body
                .appendTo('#holder');
    }
    }
})

而 XML 的格式大致如下:

<site id="0">
 <url>http://blah.com</url>
 <img>imgs/image1.png</img>
 <description>this is a description</description>
 <title>Title</title>
</site>

<site id="1">
 <url>http://filler.com</url>
 <img>imgs/image2.jpg</img>
 <description>this is another description</description>
 <title>Title 2</title>
</site>

当然,您可以将XML导入到表格或任何其他类型的HTML元素中,而不是导入到div中。

0

我刚刚也遇到了同样的问题。 我的解决方案是将包含文件直接传递到XML中。 这段代码已在Chrome、Firefox和IE 10、9、8和7下进行了测试。

function propegateIncludes(dname,xml, isExplorer) {
    var preTag = isExplorer ? "xsl:" : "";

    var TAG_STYLESHEET = preTag + "stylesheet", TAG_INCLUDE = preTag
        + "include", TAG_TEMPLATE = preTag + "template";

    var stylesheets = xml.getElementsByTagName(TAG_STYLESHEET);
    if (stylesheets.length == 0) {
        return;
    }

    var includes = xml.getElementsByTagName(TAG_INCLUDE);
    if (includes.length == 0) {
        return;
    }

    var stylesheet = stylesheets[0];
    var path = dname.substring(0, dname.lastIndexOf('/'));

    for ( var i = 0; i < includes.length; i++) {
        var args = includes[i].attributes;
        var includeXml = null;

        for ( var a = 0; a < args.length; a++) {
            if (args[a].nodeName == "href") {
                includeXml = loadXMLDoc(path + "/" + args[a].nodeValue);
                break;
            }
        }

        stylesheet.removeChild(includes[i]);

        if (includeXml != null) {
            var templates = includeXml.getElementsByTagName(TAG_TEMPLATE);

            for (var t = 0; t < templates.length; ++t) {
                stylesheet.appendChild(templates[t].cloneNode(true));
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接