您的位置:首页 > Web前端 > CSS

jaxb jaxp使用xml来生成动态页面 - xml xsd xsl ( xpath xslt ) css 概念入门

2010-08-12 17:32 761 查看

问题:

公司里面的一个项目需要建立一个Test Benchmark 来比较不同算法计算的mileage。

需求定义:

输入:预先收集的一套Test case,可以通过页面添加新的Test case

输出:不同算法在Test case上运行的结果

Solution

分析需求我们发现

只有少量的操作(页面)

结构化的数据(TestCase, TestResult)

我们的项目是运行在Tomcat
下的Axis2
的App,可以整个DB table来存储Test case和Test Result,然后使用jsp来生成动态页面。但考虑到大炮不能打蚊子的原则,存储一块我们选用xml;然后我们即可想到使用xsl来格式化(美化)xml,因为浏览器对xml是树状显示,而我们通常查看数据的方式是表格。

最终:选择使用XML instead of jsp

这里对xml的第一个概念就是:结构化数据的文件存储. 在互联网中,结构化的数据大量存在,比如酷讯网
机票的查询,垂直搜索中的商品列表,淘宝
中的商品列表。注意结构化的数据存储使用最多的就是数据库。

1. 定义XSD文件

xsd(XML Schema Definition) 是什么?

数据库中我们会有数据库的schema,就是各个表格的定义,描述了表格有哪些字段,这些字段的类型,是否为空等。XSD就是xml的schema,描述该xml文件的元素。下面的XSD例子,我们定义了测试的输入集inputSet由多个testCase组成(unbounded说明testCase在inputSet中是多个),每个testCase有四个element(trackId, start, ...)和三个属性(testCaseId)。

element vs attribute
中提到如果该information是essential material 就是用element,如果是补充说明,使用attribute。同样在stackoverflow
中提及了使用attribute的一些限制.

为什么要定义XSD,直接生成xml文件不就完事?我们生成方式无非两种:手写或程序生成。程序生成,就必须定义XSD,让程序知道如何去解析(unmarshall)/生成(marshall)xml文件。- 注意这里程序生成并不是说你使用类似于手写的方式在程序中将一个xml文件拼接出来,在解析时则使用某个xml parser(dom, jdom, sax, dom4j)将xml parser并转换成你的java 对象。传统的方式:

生成:类手写式拼接出xml

解析:xml parser -> dom tree(or parser event) -> your target java object

可以看到,这种方式一是容易出错,二是不够高效。

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="inputSet">
<xs:complexType>
<xs:sequence>
<xs:element ref="testCase" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="testCase">
<xs:complexType>
<xs:sequence>
<xs:element name="trackId" type="xs:string" />
<xs:element name="start" type="xs:string" />
<xs:element name="end" type="xs:string" />
<xs:element name="mileage" type="xs:double" />
</xs:sequence>
<xs:attribute name="testCaseId" type="xs:int" use="required" />
<xs:attribute name="ptn" type="xs:string" use="optional" />
<xs:attribute name="description" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
<xs:element name="resultSet">
<xs:complexType>
<xs:sequence>
<xs:element ref="testRecord" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="testRecord">
<xs:complexType>
<xs:sequence>
<xs:element name="testCaseId" type="xs:int" />
<xs:element name="algoName" type="xs:string" />
<xs:element name="mileage" type="xs:double" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>


2. 使用JAXB 编译xsd,生成相应java class

JAXB
(Java Architecture for XML Binding
)是一些流行框架Castor , jBind

被JDK吸纳的产物。有了JAXB,我们只需要要定义个xsd,然后可以自动生成java class。使用JAXB的marshaller和unmarshaller,非常快速简洁的生成/解析xml文件。JAXB技术简而言之:

XSD 和 java class 之间的相互转换

java对象实例和xml文件之间的相互转换:由java对象实例直接生成xml文件;解析xml文件至java对象实例

我们指定class所在的package,后文中生成jaxb context对象使用此package name

xjc -d . -p com.xx.benchmark TestCase.xsd


3. 使用JAXB来marshall和unmarshall xml文件,xml文件和java对象实例相互转换

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<inputSet>
<testCase description="" ptn="111" testCaseId="1">
<trackId>111</trackId>
<start>2010-06-23 6:14pm PDT</start>
<end>2010-06-23 6:34pm PDT</end>
<mileage>7.7</mileage>
</testCase>
<testCase description="" ptn="111" testCaseId="2">
<trackId>111</trackId>
<start>2010-08-08 04:21PM PDT</start>
<end>2010-08-08 11:08PM PDT </end>
<mileage>107.4</mileage>
</testCase>
</inputSet>


解析和生成xml文件,都只需要短短两行代码,this is life, so easy?

//create jaxb context
JAXBContext jc = JAXBContext.newInstance(packageName);
//xml -> java instance
Unmarshaller u = jc.createUnmarshaller();
InputSet inputSet = (InputSet) u.unmarshal(new File(path));'
//java instance -> xml
Marshaller m = jc.createMarshaller();
m.marshal(inputSet, new File(path));


4. 前问我们提到,在浏览器中打开xml文件,呈现树状浏览, 于是想要用xsl
来改变xml显示方式

如同css来美化html文件一样,xsl
(eXtensible Style sheet Language)就是xml的样式语言。css中有选择器,让你选择你在那些html元素上使用定义的样式,xsl有xpath
来导航定位xml元素。css中有样式表,如font-size, font-family, width等,xml中有xslt(XSL Transformations)将xml转换成xhtml(html)等其他文档,XSL-FO(本文中并未使用)来format。

(问:如果我们能将xml转换成html,而html又可以使用css样式,为什么还需要XSL-FO)

4.1 定义xsl文件

要定义xsl文件,需要使用xpath来定位、遍历xml中的节点

xpath
是用来在xml中导航定位,所有path中第一个概念就是relative和absolute, relative是基于前节点(context node)开始,root是基本xml的文档根节点。

有了基准点,再理解xpath中的第二个概念:Axes. 这里的轴就是你需要定位的节点和当前节点的关系,有祖先(ancestor),后代(descendant),父亲(parent),孩子(child).....。

前两个概念都很好理解,再引入第三个好理解的概念:谓词(predicate)。谓词就是逻辑测试。我们从一个数组里面取元素是使用index,比如a[1]取第一个元素,xpath你可以将index变成谓词,比如a[ . > 100]将数组中值大于100的元素取出来。谓词给了我们除index之外更丰富的语义选择。

将后两个概念再加一个node test(一般是name test),就是一个xpath的定位(Step),给出几个例子:

选取当前节点的最后一个para子节点

选取当前节点的第5个type为'warning'的para子节点

选取当前节点的第5个para子节点,测试其type是否为'warning',如果是则返回,不是则不返回

选取当前节点的所有chapter子节点,使得有title子节点的内容为'Introduction'

child::para[position()=last()]
child::para[attribute::type='warning'][position()=5]
child::para[position()=5][attribute::type="warning"]
child::chapter[child::title='Introduction']


最后xpath要知道的核心概念就是几个简写和一些函数

//

is short for
/descendant-or-self::node()/


/ document

.

is short for
self::node()


..

is short for
parent::node()


position() 注意在preceding sibling
时position的返回

last()

id()

count()

name()

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- http://www.jenitennison.com/xslt/grouping/muenchian.html -->
<xsl:template match="inputSet">
<html>
<head>
<mce:style><!--
table
{
border-collapse:collapse;
border-spacing:0;
border-left:1px solid
#000;
border-top:1px solid #000;
font-size:12px;
font-family:sans-serif,Arial;
}
th,td{
border-right:1px solid#000;
border-bottom:1px
solid #000;
padding:5px 15px;
}
th
{font-weight:bold;background:#ccc;
}

--></mce:style><style mce_bogus="1">					table
{
border-collapse:collapse;
border-spacing:0;
border-left:1px solid
#000;
border-top:1px solid #000;
font-size:12px;
font-family:sans-serif,Arial;
}
th,td{
border-right:1px solid#000;
border-bottom:1px
solid #000;
padding:5px 15px;
}
th
{font-weight:bold;background:#ccc;
}
</style>
</head>
<body>
<h3>Mileage Test Case</h3>
<table>
<!-- header  -->
<xsl:for-each select="testCase[position()=1]">
<tr>
<th> testCaseId	</th>
<xsl:for-each select="attribute::*">
<xsl:if test="not(position()=last())">
<th>
<xsl:value-of select="name()" />
</th>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="child::*">
<th>
<xsl:value-of select="name()" />
</th>
</xsl:for-each>
</tr>
</xsl:for-each>
<!-- data  -->
<xsl:for-each select="testCase">
<!--		<xsl:sort select="testCaseId" /> -->
<tr>
<th>
<xsl:value-of select="attribute::testCaseId" />
</th>
<xsl:for-each select="attribute::*">
<xsl:if test="not(position()=last())">
<td>
<xsl:value-of select="." />
</td>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="child::*">
<td>
<xsl:value-of select="." />
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>


上述xsl就是将前面的xml文件转换成一个html 表格,表格头的生成(第一行)是取出第一个testCase,将其属性和child element的name通过<xsl:value-of>输出。然后是遍历所有testCase,将其属性和child element的值输出成一行。

其中我们为了将testCaseId放在第一个列,进行了区别对待。

上述xsl文件的另外一部分:xslt
,我们仅仅简单叙述三步走原则:使用<xsl:template>来匹配xml元素,使用<xsl:for-each>来遍历,使用<xsl-value-of>来打印输出。

4.2 关联xsl文件

在xml中加入xml-stylesheet声明即可

<?xml version='1.0'?>
<?xml-stylesheet type='text/xsl' href='TestResult.xsl' ?>


由于我们的TestResult xml文件是由java class marshall自动生成,为了避免生成xml文件后再手工修改,我们使用了Marshaller.JAXB_FRAGMENT, Boolean.TRUE 选项

Marshaller m = TestCaseService.getMarshaller();
m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
StringWriter sw = new StringWriter();
sw.append("<?xml version='1.0'?>");
sw.append("<?xml-stylesheet type='text/xsl' href='TestResult.xsl' ?>");
ResultSet set = new ResultSet();
set.testRecord = records;
m.marshal(set, sw);
File f = new File(path);
System.out.println("save test result to: " + f.getAbsolutePath());
FileWriter fw = new FileWriter(f);
fw.write(sw.getBuffer().toString());
fw.close();


5. 在使用xsl时,我们遇到了一个问题,就是向xsl传入参数。我们使用google chart生成了如下图片,并希望把这个图片和xml中的数据一起显示在一个页面中。由于图片的url是动态生成,所以只能作为参数传入xsl



5.1 借助jaxp中的java xml transformat api来向xsl传入参数

JAXP(java api for xml processing
) 包含xml parser API(xml <-> node (event)之间的相互转换)和xslt API

java xml transformat 可以将xml文件根据指定的xsl文件,转换成一个目标文件(往往是html文件)

TransformerFactory transformerFactory = TransformerFactory
.newInstance();
StreamSource source = new StreamSource(xmlFile);
StreamResult result = new StreamResult(htmlFile);
StreamSource style = new StreamSource(TestCaseService
.getTestResultXslFile());
Transformer transformer = transformerFactory.newTransformer(style);
for (int i = 0; i < parameter.length / 2; i++) {
transformer.setParameter(parameter[i * 2], parameter[i * 2 + 1]);
}
transformer.transform(source, result);


5.2 在xsl文件中使用<xsl:param>声明并使用此变量

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="chart">
no chart
</xsl:param>
<xsl:param name="t">
0.1
</xsl:param>
<xsl:key name="record-by-algo" match="testRecord" use="algoName" />
<xsl:key name="record-by-tid" match="testRecord" use="testCaseId" />
<!-- http://www.jenitennison.com/xslt/grouping/muenchian.html -->
<xsl:template match="resultSet">
<html>
<head>
<mce:style><!--
table
{
border-collapse:collapse;
border-spacing:0;
border-left:1px solid
#000;
border-top:1px solid #000;
font-size:12px;
font-family:sans-serif,Arial;
}
th,td{
border-right:1px solid#000;
border-bottom:1px
solid #000;
padding:5px
15px;
}
th
{font-weight:bold; background:rgb(227,227,227);
}
td.accept
{
background:#9ACD32;
}
td.naccept
{
background:#F2F7E6;
}

--></mce:style><style mce_bogus="1">					table
{
border-collapse:collapse;
border-spacing:0;
border-left:1px solid
#000;
border-top:1px solid #000;
font-size:12px;
font-family:sans-serif,Arial;
}
th,td{
border-right:1px solid#000;
border-bottom:1px
solid #000;
padding:5px
15px;
}
th
{font-weight:bold; background:rgb(227,227,227);
}
td.accept
{
background:#9ACD32;
}
td.naccept
{
background:#F2F7E6;
}
</style>
</head>
<body>
<h3>Mileage Algo Compare</h3>
<xsl:value-of select='$chart' disable-output-escaping="yes" />
<br />
<table>
<!-- header  -->
<tr>
<th>caseId</th>
<xsl:for-each
select="testRecord[generate-id() =
generate-id(key('record-by-algo', algoName)[1])]">
<!--  		<xsl:sort select="position()" order="descending" /> -->
<th>
<xsl:value-of select="algoName" />
</th>
</xsl:for-each>
</tr>
<!-- data  -->
<xsl:for-each
select="testRecord[count(. | key('record-by-tid', testCaseId)[1]) = 1]">
<!--		<xsl:sort select="testCaseId" /> -->
<tr>
<th>
<xsl:value-of select="testCaseId" />
</th>
<xsl:for-each select="key('record-by-tid', testCaseId)">
<xsl:sort select="algoName" />
<xsl:if test="algoName = 'Odometer'">
<td class="naccept">
<xsl:value-of select="mileage" />
</td>
</xsl:if>
<xsl:if test="not(algoName = 'Odometer')">
<xsl:variable name="sMileage">
<xsl:value-of
select="../testRecord[testCaseId = current()/testCaseId][algoName='Odometer']/mileage" />
</xsl:variable>
<xsl:variable name="diff">
<xsl:value-of select="(mileage - $sMileage) div $sMileage" />
</xsl:variable>
<xsl:if test="($diff <= $t and $diff >= -$t) ">
<td class="accept">
<xsl:value-of select="mileage" />
</td>
</xsl:if>
<xsl:if test="not($diff <= $t and $diff >= -$t) ">
<td class="naccept">
<xsl:value-of select="mileage" />
</td>
</xsl:if>
</xsl:if>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>


6. 生成html文件后,似乎万事大吉,可以直接在浏览器里面查看google chart和表格数据

但表格数据字体以及边框太丑,于是乎又需要用到css来美化html.

css 很复杂,但化繁就简,就是选择器
加样式, 表格css

<head>
<mce:style><!--
body {
font-size: 8px;
font-family: sans-serif, Arial;
}
.texta {
width: 200px;
}
td {
text-align: left;
}
tr.option {
font-style: italic;
}
--></mce:style><style mce_bogus="1">body {
font-size: 8px;
font-family: sans-serif, Arial;
}
.texta {
width: 200px;
}
td {
text-align: left;
}
tr.option {
font-style: italic;
}</style>
</head>


7. 由于我们的应用是部署在tomcat中(的axis2应用),servlet 相关:

7.1 redirection

response.sendRedirect(response.encodeRedirectURL("list"));


7.2 relative path absolute path

absolute path 是根路径,http://127.0.0.1/

relative path 是相对于当前url的路径,当前是http://127.0.0.1/app/page1,如果写page2,则变成http://127.0.0.1/app/page1/page2,所以往往导航时往往使用context path( http://127.0.0.1/app), context path + page2

最终我们得到的页面如下:
http://127.0.0.1:8080/mileage/testharness

http://127.0.0.1:8080/mileage/testharness/20


问题回复

http://topic.csdn.net/u/20101112/14/4b86e05d-09c7-442c-8365-bc6a04e73501.html

你的代码是典型的传统写法,

1) 从数据库中取出数据

2) 将取出的数据ResultSet,生成一个xml文件。

每行数据对应一个xml中的一个数据节点

如果我来处理

1) 对着数据库的schema(表格定义)

手写xml的schema(xsd文件)

2) 使用JAXB,将xsd编译生成java class

3) 从数据库中取出数据后,将每行数据转换成一个java对象

4) 将java对象marshall到文件(xml文件)

(如何marshall,参考上文)

表格太多时,第一步需要程序/工具自动生成

1) 根据数据库的schema自动生成xml的shema

我到没整过,我只整过从xml schema到数据库的schema,使用的是

castor.

刚刚搜了下,你可参考
http://www.oxygenxml.com/database_to_schema.html
当然,你解决问题的思路是:

1) 从xml文件生成xsd定义

这里有一篇参考
http://www.dotkam.com/2008/05/28/generate-xsd-from-xml/
似乎前面的oxygenxml也可以做这样的事情。

一般都是从xsd->xml, 因为xsd好比数据库的表格定义,只需生成一次即可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: