13.10 使用XML文件——PLIST以及其他格式保存配置

    正如在第9章“序列化和保存——JSON、YAML、Pickle、CSV和XML”中所看到的,Python的xml包中提供了多个用于解析 XML 文件模块。由于XML文件使用的普遍性,在XML文档与Python对象间的转换通常是必要的。不像JSON和YAML,XML的转换不是那么容易。

    一种常见的方式是使用XML将配置数据表示在.plist文件中。更多有关.plist格式的信息,参见http://developer.apple.com/documentation/Darwin/

    Reference/ManPages/ man5/plist.5.html

    苹果用户可以使用man plist命令来查看这个页面。.plist格式的优势是它使用了一些非常一般化的标签,这使得.plist文件的创建和解析很容易。以下的例子中包含了配置参数的.plist文件。

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.
    apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
     <key>player</key>
     <dict>
       <key>betting</key>
       <string>Flat</string>
       <key>play</key>
       <string>SomeStrategy</string>
       <key>rounds</key>
       <integer>100</integer>
       <key>stake</key>
       <integer>50</integer>
     </dict>
     <key>simulator</key>
     <dict>
       <key>outputfile</key>
       <string>p2_c13_simulation8.dat</string>
       <key>samples</key>
       <integer>100</integer>
     </dict>
     <key>table</key>
     <dict>
       <key>dealer</key>
       <string>Hit17</string>
       <key>decks</key>
       <integer>6</integer>
       <key>limit</key>
       <integer>50</integer>
       <key>payout</key>
       <array>
         <integer>3</integer>
         <integer>2</integer>
       </array>
       <key>split</key>
       <string>NoResplitAces</string>
     </dict>
    </dict>
    </plist>

    在这个例子中,演示了嵌套的字典结构,有很多与Python类型兼容的XML标签。


    Python类型

    Plist标签

    str

    <string>

    float

    <real>

    int

    <integer>

    datetime

    <date>

    boolean

    <true/>或<false/>

    bytes

    <data>

    list

    <array>

    dict

    <dict>

    正如在之前的例子中,<key>的值是字符串。这样就为模拟器应用提供了编码良好的plist,其中包含了配置参数。可使用相对容易的方式来加载一个.plist

    import plistlib
    print( plistlib.readPlist(plist_file) )

    这将重构配置参数。然后可以使用之前在介绍JSON配置文件的节中提到的main_nested _dict()函数来操作这个嵌套的字典。

    当使用单一模块函数来解析文件时,.plist格式显得非常好用。与JSON或特性文件一样,缺少对Python自定义类的支持。

    自定义XML配置文件

    有关更复杂的XML配置文件,可以参见http://wiki.metawerx.net/wiki/Web.xml。这个文件中同时包含了特殊标签和一般标签,这些文档解析起来很困难,这里有两种常见的方式。

    • 写一个文档处理类,使用XPath,根据数据完成对标签的查询。这样的话,就需要写特性(或方法)进行查询XML结构中所需要的信息。
    • 将XML文档转换为一个Python的数据结构,这种方式和之前看到的plist模块是一致的。

    基于Web.xml文件的例子,在配置模拟器应用时,可以设计自定义的XML文档。

    <?xml version="1.0" encoding="UTF-8"?>
    <simulation>
       <table>
         <dealer>Hit17</dealer>
         <split>NoResplitAces</split>
         <decks>6</decks>
         <limit>50</limit>
         <payout>(3,2)</payout>
       </table>
       <player>
         <betting>Flat</betting>
         <play>SomeStrategy</play>
         <rounds>100</rounds>
         <stake>50</stake>
       </player>
       <simulator>
         <outputfile>p2_c13_simulation11.dat</outputfile>
         <samples>100</samples>
       </simulator>
    </simulation>

    这是一个特殊化的 XML 文件。我们没有提供 DTD 或 XSD,因此没有使用根据模型来验证XML的方式。然而,这个文件非常小,很容易调试,并且初始化过程与之前例子中提到的类似。这里是一个Configuration类,使用XPath查询来从文件中获取信息。

    import xml.etree.ElementTree as XML
    class Configuration:
      def readfile( self, file ):
         self.config= XML.parse( file )
      def read( self, filename ):
         self.config= XML.parse( filename )
      def read_string( self, text ):
         self.config= XML.fromstring( text )
      def get( self, qual_name, default ):
         section,
    , item = qualname.partition(".")
         query= "./{0}/{1}".format( section, item )
         node= self.config.find(query)
         if node is None: return default
         return node.text
      def _getitem
    ( self, section ):
         query= "./{0}".format(section)
         parent= self.config.find(query)
         return dict( (item.tag, item.text) for item in parent )

    我们实现了3种方法来加载XML文档:read()read_file()read_string()。它们都是对xml.etree.ElementTree类中方法的代理实现,这与configparser API是一致的。我们也会使用load()loads(),因为它们也是相应的对parse()fromstring()的代理实现。

    至于配置数据的访问,我们实现了两种方法:get()getitem()。可以像这样来使用get()方法:stake= int(config.get('player.stake', 50))以及像这样来使用getitem()方法:stake= config['player']['stake']

    解析过程比.plist文件稍微复杂些。然而,XML比相同内容的.plist格式相对简单。

    可以对特性文件使用在之前的节中所介绍的main_cm_str()函数来完成配置的处理过程。