如何使用 XSLT 转换 XML?
How to transform an XML using an XSLT?
我有一个 XML 文件和一个 XSLT,我想使用 XSLT 将 XML 转换为 HTML 字符串,该字符串可以加载到 TWebBrowser
.
对于我的测试,我使用 these 个示例文件。
XML 文件:
<?xml version="1.0" encoding="UTF-8"?>
<breakfast_menu>
<food>
<name>Belgian Waffles</name>
<price>.95</price>
<description>Two of our famous Belgian Waffles with plenty of real maple syrup</description>
<calories>650</calories>
</food>
<food>
<name>Strawberry Belgian Waffles</name>
<price>.95</price>
<description>Light Belgian waffles covered with strawberries and whipped cream</description>
<calories>900</calories>
</food>
<food>
<name>Berry-Berry Belgian Waffles</name>
<price>.95</price>
<description>Light Belgian waffles covered with an assortment of fresh berries and whipped cream</description>
<calories>900</calories>
</food>
<food>
<name>French Toast</name>
<price>.50</price>
<description>Thick slices made from our homemade sourdough bread</description>
<calories>600</calories>
</food>
<food>
<name>Homestyle Breakfast</name>
<price>.95</price>
<description>Two eggs, bacon or sausage, toast, and our ever-popular hash browns</description>
<calories>950</calories>
</food>
</breakfast_menu>
XSLT 文件:
<?xml version="1.0" encoding="UTF-8"?>
<html xsl:version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<body style="font-family:Arial;font-size:12pt;background-color:#EEEEEE">
<xsl:for-each select="breakfast_menu/food">
<div style="background-color:teal;color:white;padding:4px">
<span style="font-weight:bold"><xsl:value-of select="name"/> - </span>
<xsl:value-of select="price"/>
</div>
<div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
<p>
<xsl:value-of select="description"/>
<span style="font-style:italic"> (<xsl:value-of select="calories"/> calories per serving)</span>
</p>
</div>
</xsl:for-each>
</body>
</html>
尝试 1:
我找到了 this 解决方案并尝试了以下功能:
Uses
XMLDoc, XMLIntf;
function Transform(XMLContent : string; XSLContent : string) : WideString;
var
XML : IXMLDocument;
XSL : IXMLDocument;
begin
XML := LoadXMLData(XMLContent);
XSL := LoadXMLData(XSLContent);
XML.DocumentElement.TransformNode(XSL.DocumentElement, Result)
end;
但它产生了意外的输出:
Belgian Waffles.95Two of our
famous Belgian Waffles with plenty of real maple syrup650Strawberry
Belgian Waffles.95Light Belgian waffles covered with strawberries
and whipped cream900Berry-Berry Belgian Waffles.95Light Belgian
waffles covered with an assortment of fresh berries and whipped
cream900French Toast.50Thick slices made from our homemade sourdough
bread600Homestyle Breakfast.95Two eggs, bacon or sausage, toast, and
our ever-popular hash browns950
尝试 2:
我在 this 页面的 "Using the MSXML Parser/ Transform Engine" 部分找到了以下函数(注意:这是一个 Delphi 5 示例,但我使用的是 Delphi 2007 ).
function DoTransform(const xml, xsl : string ): string;
var
XMLDoc : IXMLDOMDocument;
XSLDoc : IXMLDOMDocument;
Template : IXSLTemplate;
Processor : IXSLProcessor;
begin
Result := '';
try
XMLDoc := CoFreeThreadedDOMDocument30.Create;
XSLDoc := CoFreeThreadedDOMDocument30.Create;
XMLDoc.load(xml);
XSLDoc.load(xsl);
Template := CoXSLTemplate30.Create;
Template.stylesheet := XSLDoc;
Processor := Template.createProcessor;
Processor.input := XMLDoc;
Processor.transform;
result := Processor.output;
finally
XMLDoc := nil;
XSLDoc := nil;
end;
end;
我已经导入了 Microsoft XML 类型库...
Component -> Import Component -> Import a Type Library -> Next -> Microsoft XML, v6.0
...并在 uses 子句中添加了 MSXML2_TLB
但随后出现了一些其他错误:
E2003 Undeclared identifier: 'CoFreeThreadedDOMDocument30'
E2003 Undeclared identifier: 'CoXSLTemplate30'
我已将 CoFreeThreadedDOMDocument30
切换为 CoFreeThreadedDOMDocument60
,将 CoXSLTemplate30
切换为 CoXSLTemplate60
,现在编译没有错误。
在运行时,在这一行:
Template.stylesheet := XSLDoc;
它引发了以下异常(意大利语):
Il foglio di stile non include un elemento documento. Il foglio di
stile è vuoto oppure potrebbe essere un documento XML in formato non
corretto.
英文应该是这样的:
The stylesheet doesn't include a document element. The stylesheet is
empty or it could be a bad formatted XML document.
我用其他示例文件进行了测试,错误始终相同,但我不明白可能是哪个问题。
我的方法是否正确,或者是否有更好的方法来满足我的需求?
我有以下有效的代码。它假定一个包含 XSLT 的文件,但应该很容易更改。
很久以前写的,赶时间。所以可能还有一些改进的余地。希望对你有帮助。
uses
Windows, ComObj, XMLDoc, msxmldom, msxml;
function DOMToMSDom(const Doc: IDOMDocument): IXMLDOMDocument3;
begin
Result := ((Doc as IXMLDOMNodeRef).GetXMLDOMNode as IXMLDOMDocument3);
end;
function TransformXMLDocWithXSLTFile(const Doc: XMLIntf.IXMLDocument; const StyleSheetLocation: string; var TransformedData, Error: string): Boolean;
var
MsxmlDoc: IXMLDOMDocument3;
xslStyle : IXMLDOMDocument;
begin
Result := False;
if not FileExists(StyleSheetLocation) then
begin
Error := 'Specified XSLT stylesheet file does not exist: ' + StyleSheetLocation;
Exit;
end;
try
MsxmlDoc := DOMToMSDom(Doc.DOMDocument);
xslStyle := CoDOMDocument60.Create;
xslStyle.load(StyleSheetLocation);
IXMLDOMDocument3(xslStyle).setProperty('AllowXsltScript', True);
TransformedData := MsxmlDoc.transformNode(xslStyle);
Result := True;
except
on E: Exception do
begin
Error := E.Message;
end;
end;
end;
有一些问题;
您的 XSL 没有样式表、输出或根模板。它需要是这样的:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" encoding="UTF-8" />
<xsl:template match="/">
<html xsl:version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<body style="font-family:Arial;font-size:12pt;background-color:#EEEEEE">
<xsl:for-each select="breakfast_menu/food">
<div style="background-color:teal;color:white;padding:4px">
<span style="font-weight:bold">
<xsl:value-of select="name"/> - </span>
<xsl:value-of select="price"/>
</div>
<div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
<p>
<xsl:value-of select="description"/>
<span style="font-style:italic"> (<xsl:value-of select="calories"/> calories per serving)</span>
</p>
</div>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
在你的delphi代码中,因为你正在加载字符串,所以你应该使用.loadXML,而不是.load
XMLDoc := CoFreeThreadedDOMDocument60.Create;
XSLDoc := CoFreeThreadedDOMDocument60.Create;
XMLDoc.loadXML(xml);
XSLDoc.loadXML(xsl);
Template := CoXSLTemplate60.Create;
Template.stylesheet := XSLDoc;
Processor := Template.createProcessor;
Processor.input := XMLDoc;
Processor.transform;
s := Processor.output;
执行 .load 或 .loadXML 后,您查看解析错误以检查发生了什么(如果您遇到问题)
xsldoc.parseError.errorCode; //will be 0 if all is well
xsldoc.parseError.reason;
这是我在 Delphi 2007 和 Delphi XE7.
上测试的解决方案
uses
msxml;
function Transform(const AXMLContent : string; const AXSLContent : string) : string;
var
XML : IXMLDOMDocument;
XSL : IXMLDOMDocument;
begin
XML := CoDOMDocument.Create;
XML.loadXML(AXMLContent);
XSL := CoDOMDocument.Create;
XSL.loadXML(AXSLContent);
Result := XML.TransformNode(XSL);
end;
结果 HTML 代码:
<html>
<body style="font-family:Arial;font-size:12pt;background-color:#EEEEEE">
<div style="background-color:teal;color:white;padding:4px"><span style="font-weight:bold">Belgian Waffles - </span>.95</div>
<div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
<p>Two of our famous Belgian Waffles with plenty of real maple syrup<span style="font-style:italic"> (650 calories per serving)</span></p>
</div>
<div style="background-color:teal;color:white;padding:4px"><span style="font-weight:bold">Strawberry Belgian Waffles - </span>.95</div>
<div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
<p>Light Belgian waffles covered with strawberries and whipped cream<span style="font-style:italic"> (900 calories per serving)</span></p>
</div>
<div style="background-color:teal;color:white;padding:4px"><span style="font-weight:bold">Berry-Berry Belgian Waffles - </span>.95</div>
<div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
<p>Light Belgian waffles covered with an assortment of fresh berries and whipped cream<span style="font-style:italic"> (900 calories per serving)</span></p>
</div>
<div style="background-color:teal;color:white;padding:4px"><span style="font-weight:bold">French Toast - </span>.50</div>
<div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
<p>Thick slices made from our homemade sourdough bread<span style="font-style:italic"> (600 calories per serving)</span></p>
</div>
<div style="background-color:teal;color:white;padding:4px"><span style="font-weight:bold">Homestyle Breakfast - </span>.95</div>
<div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
<p>Two eggs, bacon or sausage, toast, and our ever-popular hash browns<span style="font-style:italic"> (950 calories per serving)</span></p>
</div>
</body>
</html>
为了将结果字符串显示到 TWebBrowser
中,我使用了以下代码:
procedure LoadHTMLCode(AWebBrowser : TWebBrowser; const AHTMLCode: string);
var
Doc: Variant;
begin
if not Assigned(AWebBrowser.Document) then
AWebBrowser.Navigate('about:blank');
Doc := AWebBrowser.Document;
Doc.Clear;
Doc.Write(AHTMLCode);
Doc.Close;
end;
...
var
XMLContent : string;
XLSContent : string;
HTMLCode : string;
begin
//loading XML content
XMLContent := ...;
//loading XLS content
XLSContent := ...;
//transforming
HTMLCode := Transform(XMLContent, XLSContent);
//displaying
LoadHTMLCode(WebBrowser1, HTMLCode);
end;
我有一个 XML 文件和一个 XSLT,我想使用 XSLT 将 XML 转换为 HTML 字符串,该字符串可以加载到 TWebBrowser
.
对于我的测试,我使用 these 个示例文件。
XML 文件:
<?xml version="1.0" encoding="UTF-8"?>
<breakfast_menu>
<food>
<name>Belgian Waffles</name>
<price>.95</price>
<description>Two of our famous Belgian Waffles with plenty of real maple syrup</description>
<calories>650</calories>
</food>
<food>
<name>Strawberry Belgian Waffles</name>
<price>.95</price>
<description>Light Belgian waffles covered with strawberries and whipped cream</description>
<calories>900</calories>
</food>
<food>
<name>Berry-Berry Belgian Waffles</name>
<price>.95</price>
<description>Light Belgian waffles covered with an assortment of fresh berries and whipped cream</description>
<calories>900</calories>
</food>
<food>
<name>French Toast</name>
<price>.50</price>
<description>Thick slices made from our homemade sourdough bread</description>
<calories>600</calories>
</food>
<food>
<name>Homestyle Breakfast</name>
<price>.95</price>
<description>Two eggs, bacon or sausage, toast, and our ever-popular hash browns</description>
<calories>950</calories>
</food>
</breakfast_menu>
XSLT 文件:
<?xml version="1.0" encoding="UTF-8"?>
<html xsl:version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<body style="font-family:Arial;font-size:12pt;background-color:#EEEEEE">
<xsl:for-each select="breakfast_menu/food">
<div style="background-color:teal;color:white;padding:4px">
<span style="font-weight:bold"><xsl:value-of select="name"/> - </span>
<xsl:value-of select="price"/>
</div>
<div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
<p>
<xsl:value-of select="description"/>
<span style="font-style:italic"> (<xsl:value-of select="calories"/> calories per serving)</span>
</p>
</div>
</xsl:for-each>
</body>
</html>
尝试 1:
我找到了 this 解决方案并尝试了以下功能:
Uses
XMLDoc, XMLIntf;
function Transform(XMLContent : string; XSLContent : string) : WideString;
var
XML : IXMLDocument;
XSL : IXMLDocument;
begin
XML := LoadXMLData(XMLContent);
XSL := LoadXMLData(XSLContent);
XML.DocumentElement.TransformNode(XSL.DocumentElement, Result)
end;
但它产生了意外的输出:
Belgian Waffles.95Two of our famous Belgian Waffles with plenty of real maple syrup650Strawberry Belgian Waffles.95Light Belgian waffles covered with strawberries and whipped cream900Berry-Berry Belgian Waffles.95Light Belgian waffles covered with an assortment of fresh berries and whipped cream900French Toast.50Thick slices made from our homemade sourdough bread600Homestyle Breakfast.95Two eggs, bacon or sausage, toast, and our ever-popular hash browns950
尝试 2:
我在 this 页面的 "Using the MSXML Parser/ Transform Engine" 部分找到了以下函数(注意:这是一个 Delphi 5 示例,但我使用的是 Delphi 2007 ).
function DoTransform(const xml, xsl : string ): string;
var
XMLDoc : IXMLDOMDocument;
XSLDoc : IXMLDOMDocument;
Template : IXSLTemplate;
Processor : IXSLProcessor;
begin
Result := '';
try
XMLDoc := CoFreeThreadedDOMDocument30.Create;
XSLDoc := CoFreeThreadedDOMDocument30.Create;
XMLDoc.load(xml);
XSLDoc.load(xsl);
Template := CoXSLTemplate30.Create;
Template.stylesheet := XSLDoc;
Processor := Template.createProcessor;
Processor.input := XMLDoc;
Processor.transform;
result := Processor.output;
finally
XMLDoc := nil;
XSLDoc := nil;
end;
end;
我已经导入了 Microsoft XML 类型库...
Component -> Import Component -> Import a Type Library -> Next -> Microsoft XML, v6.0
...并在 uses 子句中添加了 MSXML2_TLB
但随后出现了一些其他错误:
E2003 Undeclared identifier: 'CoFreeThreadedDOMDocument30'
E2003 Undeclared identifier: 'CoXSLTemplate30'
我已将 CoFreeThreadedDOMDocument30
切换为 CoFreeThreadedDOMDocument60
,将 CoXSLTemplate30
切换为 CoXSLTemplate60
,现在编译没有错误。
在运行时,在这一行:
Template.stylesheet := XSLDoc;
它引发了以下异常(意大利语):
Il foglio di stile non include un elemento documento. Il foglio di stile è vuoto oppure potrebbe essere un documento XML in formato non corretto.
英文应该是这样的:
The stylesheet doesn't include a document element. The stylesheet is empty or it could be a bad formatted XML document.
我用其他示例文件进行了测试,错误始终相同,但我不明白可能是哪个问题。
我的方法是否正确,或者是否有更好的方法来满足我的需求?
我有以下有效的代码。它假定一个包含 XSLT 的文件,但应该很容易更改。
很久以前写的,赶时间。所以可能还有一些改进的余地。希望对你有帮助。
uses
Windows, ComObj, XMLDoc, msxmldom, msxml;
function DOMToMSDom(const Doc: IDOMDocument): IXMLDOMDocument3;
begin
Result := ((Doc as IXMLDOMNodeRef).GetXMLDOMNode as IXMLDOMDocument3);
end;
function TransformXMLDocWithXSLTFile(const Doc: XMLIntf.IXMLDocument; const StyleSheetLocation: string; var TransformedData, Error: string): Boolean;
var
MsxmlDoc: IXMLDOMDocument3;
xslStyle : IXMLDOMDocument;
begin
Result := False;
if not FileExists(StyleSheetLocation) then
begin
Error := 'Specified XSLT stylesheet file does not exist: ' + StyleSheetLocation;
Exit;
end;
try
MsxmlDoc := DOMToMSDom(Doc.DOMDocument);
xslStyle := CoDOMDocument60.Create;
xslStyle.load(StyleSheetLocation);
IXMLDOMDocument3(xslStyle).setProperty('AllowXsltScript', True);
TransformedData := MsxmlDoc.transformNode(xslStyle);
Result := True;
except
on E: Exception do
begin
Error := E.Message;
end;
end;
end;
有一些问题;
您的 XSL 没有样式表、输出或根模板。它需要是这样的:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" encoding="UTF-8" />
<xsl:template match="/">
<html xsl:version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<body style="font-family:Arial;font-size:12pt;background-color:#EEEEEE">
<xsl:for-each select="breakfast_menu/food">
<div style="background-color:teal;color:white;padding:4px">
<span style="font-weight:bold">
<xsl:value-of select="name"/> - </span>
<xsl:value-of select="price"/>
</div>
<div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
<p>
<xsl:value-of select="description"/>
<span style="font-style:italic"> (<xsl:value-of select="calories"/> calories per serving)</span>
</p>
</div>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
在你的delphi代码中,因为你正在加载字符串,所以你应该使用.loadXML,而不是.load
XMLDoc := CoFreeThreadedDOMDocument60.Create;
XSLDoc := CoFreeThreadedDOMDocument60.Create;
XMLDoc.loadXML(xml);
XSLDoc.loadXML(xsl);
Template := CoXSLTemplate60.Create;
Template.stylesheet := XSLDoc;
Processor := Template.createProcessor;
Processor.input := XMLDoc;
Processor.transform;
s := Processor.output;
执行 .load 或 .loadXML 后,您查看解析错误以检查发生了什么(如果您遇到问题)
xsldoc.parseError.errorCode; //will be 0 if all is well
xsldoc.parseError.reason;
这是我在 Delphi 2007 和 Delphi XE7.
上测试的解决方案uses
msxml;
function Transform(const AXMLContent : string; const AXSLContent : string) : string;
var
XML : IXMLDOMDocument;
XSL : IXMLDOMDocument;
begin
XML := CoDOMDocument.Create;
XML.loadXML(AXMLContent);
XSL := CoDOMDocument.Create;
XSL.loadXML(AXSLContent);
Result := XML.TransformNode(XSL);
end;
结果 HTML 代码:
<html>
<body style="font-family:Arial;font-size:12pt;background-color:#EEEEEE">
<div style="background-color:teal;color:white;padding:4px"><span style="font-weight:bold">Belgian Waffles - </span>.95</div>
<div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
<p>Two of our famous Belgian Waffles with plenty of real maple syrup<span style="font-style:italic"> (650 calories per serving)</span></p>
</div>
<div style="background-color:teal;color:white;padding:4px"><span style="font-weight:bold">Strawberry Belgian Waffles - </span>.95</div>
<div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
<p>Light Belgian waffles covered with strawberries and whipped cream<span style="font-style:italic"> (900 calories per serving)</span></p>
</div>
<div style="background-color:teal;color:white;padding:4px"><span style="font-weight:bold">Berry-Berry Belgian Waffles - </span>.95</div>
<div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
<p>Light Belgian waffles covered with an assortment of fresh berries and whipped cream<span style="font-style:italic"> (900 calories per serving)</span></p>
</div>
<div style="background-color:teal;color:white;padding:4px"><span style="font-weight:bold">French Toast - </span>.50</div>
<div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
<p>Thick slices made from our homemade sourdough bread<span style="font-style:italic"> (600 calories per serving)</span></p>
</div>
<div style="background-color:teal;color:white;padding:4px"><span style="font-weight:bold">Homestyle Breakfast - </span>.95</div>
<div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
<p>Two eggs, bacon or sausage, toast, and our ever-popular hash browns<span style="font-style:italic"> (950 calories per serving)</span></p>
</div>
</body>
</html>
为了将结果字符串显示到 TWebBrowser
中,我使用了以下代码:
procedure LoadHTMLCode(AWebBrowser : TWebBrowser; const AHTMLCode: string);
var
Doc: Variant;
begin
if not Assigned(AWebBrowser.Document) then
AWebBrowser.Navigate('about:blank');
Doc := AWebBrowser.Document;
Doc.Clear;
Doc.Write(AHTMLCode);
Doc.Close;
end;
...
var
XMLContent : string;
XLSContent : string;
HTMLCode : string;
begin
//loading XML content
XMLContent := ...;
//loading XLS content
XLSContent := ...;
//transforming
HTMLCode := Transform(XMLContent, XLSContent);
//displaying
LoadHTMLCode(WebBrowser1, HTMLCode);
end;