XML之所以应用广泛应该是因为其通用性,Web service、跨平台数据转换等经常采用XML格式。最近采用XML作为中间格式实现了一个较大数据量的迁移(与下面示例无关),在此总结一下大致用法,当然这里只涉及XML相关的部分,忽略了sed, awk之类的辅助工具。希望您能指出用法的不当之处,或更好的方法。

完整示例

1. XML Schema – 定义数据结构

XML Schema与DTD类似,都是用于定义XML文档结构,但比DTD更加灵活、更加强大。有趣的是XSD其本身也采用XML描述,因此XSD的结构是用DTD来定义(http://www.w3.org/2001/XMLSchema.dtd)。

blog_post.xsd示例文档:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns="http://tangobean.com" 
    xmlns:fwso="http://tangobean.com" 
    elementFormDefault="qualified" 
    targetNamespace="http://tangobean.com" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="posts">
        <xs:complexType>
            <xs:sequence minOccurs="0" maxOccurs="unbounded">
                <xs:element name="post" type="blogPost" />
            </xs:sequence>
        </xs:complexType>
        <xs:unique name="uniqueIssueID">
            <xs:selector xpath="fwso:post" />
            <xs:field xpath="fwso:id" />
        </xs:unique>
    </xs:element>
    <xs:complexType name="blogPost">
        <xs:sequence>
            <xs:element name="id" type="xs:integer" />
            <xs:element name="title" type="xs:string" />
            <xs:element name="author" type="xs:string" />
            <xs:element name="body" type="xs:string" />
            <xs:element name="publish_time" type="timestamp" />
            <xs:element name="tags" minOccurs="0" maxOccurs="unbounded">
                <xs:complexType>
                    <xs:sequence minOccurs="0" maxOccurs="unbounded">
                        <xs:element name="tag" type="xs:string" />
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
        </xs:sequence>
    </xs:complexType>
    <xs:simpleType name="timestamp">
        <xs:restriction base="xs:string">
            <xs:pattern value="[1-9][0-9]{9}" />
        </xs:restriction>
    </xs:simpleType>
</xs:schema>

该XSD定义了一个简单的博客文章的结构,integer, string等类型是XSD定义的数据类型,XSD可以自定义数据类型,如该示例中的timestamp。

2. xmllint – XML文档验证

xmllint事实上是一个XML解析工具,可以用于XML文档格式验证、格式化等。这里使用xmllint来验证XML文档是否符合XSD的定义:

xmllint -noout -schema blog_post.xsd posts.xml

如果验证通过,则会输出:

posts.xml validates

否则会输出对应的错误,类似:

posts.xml:11: element newtag: Schemas validity error : Element '{http://tangobean.com}newtag': This element is not expected. Expected is ( {http://tangobean.com}tags ).
posts.xml:13: element post: Schemas validity error : Element '{http://tangobean.com}post': Duplicate key-sequence ['210'] in unique identity-constraint '{http://tangobean.com}uniqueIssueID'.
posts.xml fails to validate

post.xml示例内容如下:

<?xml version="1.0"?>
<posts xmlns="http://tangobean.com"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://tangobean.com blog_post.xsd">
    <post>
        <id>210</id>
        <title>blog post 1</title>
        <author>James Tang</author>
        <body><![CDATA[<h1>what's this</h1><p>goes here</p>]]></body>
        <publish_time>1346221680</publish_time>
        <newtag>hello</newtag>
    </post>
    <post>
        <id>210</id>
        <title>blog post 2</title>
        <author>James Tang</author>
        <body><![CDATA[<h1>what's this</h1><p>goes here</p>]]></body>
        <publish_time>1346221680</publish_time>
        <tags>
            <tag>tag1</tag>
            <tag>tag2</tag>
        </tags>
    </post>
</posts>

3. xQeury – 转换数据

xQuery能够非常方便地将其它格式的XML文档转换成目标的XML格式。

blog_post.xquery示例如下:

xquery version "1.0";

declare default element namespace "http://tangobean.com"
declare namespace fwso="http://tangobean.com";
declare variable $fwso:oldDoc as xs:string := "olddoc.xml";

declare variable $fwso:oldTagDoc as xs:string := "oldtagdoc.xml";

declare function fwso:list-posts($maindoc as xs:string, $tagdoc as xs:string) {
        let $doc := doc($maindoc)
        let $seq := $doc//article
        let $tags := doc($tagdoc)

        for $item in $seq
            let $id := data($item/article_id)
            let $ptags := $tags//tag[article_id=$id]

            return 
           <post>
            <id>{$id}</id>
            <title>{data($item/article_title)}</title>
            <author>{data($item/article_author)}</author>
            <body>{data($item/article_content)}</body>
            <publish_time>{data($item/published)}</publish_time>
            {if (count($ptags) gt 0)
            then
            <tags>
                {for $tag in $ptags
                    return <tag>{data($tag/tag_name)}</tag>
                }    
            </tags>
            else
                ''
            }
           </post>
};

<posts
xmlns="http://tangobean.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://tangobean.com blog_post.xsd"
>
    {fwso:list-posts($fwso:oldDoc, $fwso:oldTagDoc)}
</posts>

4. XQilla/Zorba – xQuery解析器

xqilla blog_post.xquery > posts.xml

zorba -f -q blog_post.xquery > posts.xml

5. 参考

  1. http://www.w3.org/TR/xmlschema11-1/
  2. http://www.w3.org/TR/xmlschema11-2/
  3. http://www.w3.org/TR/xquery/
  4. http://www.zorba-xquery.com/html/index
  5. http://xqilla.sourceforge.net/HomePage