http://www.cnblogs.com/xuxm2007/archive/2011/01/16/1936610.htmlhtml
http://blog.csdn.net/ywchen2000/archive/2006/07/04/876742.aspx node
http://blog.csdn.net/zhangj1012003_2007/archive/2010/04/23/5514807.aspxpython
http://blog.csdn.net/zhangj1012003_2007/archive/2010/04/22/5514929.aspxapp
http://blog.csdn.net/zhangj1012003_2007/archive/2010/04/22/5514935.aspxdom
一.XML的讀取.
在 NewEdit 中有代碼片斷的功能,代碼片斷分爲片斷的分類和片斷的內容。在缺省狀況下都是用XML格式保存的。下面我講述一下,如何使用minidom來讀取和保存XML文件。編輯器
下面是片斷分類的一個示例文件--catalog.xml函數
<?xml version="1.0" encoding="utf-8"?>
<catalog>
<maxid>4</maxid>
<item id="1">
<caption>Python</caption>
<item id="4">
<caption>測試</caption>
</item>
</item>
<item id="2">
<caption>Zope</caption>
</item>
</catalog>post
分類是樹狀結構,顯示出來可能爲:測試
先簡單介紹一下XML的知識,若是你已經知道了能夠跳過去。
1. XML文檔的編碼
此XML文檔的編碼爲utf-8,所以你看到的「測試」實際上是UTF-8編碼。在XML文檔的處理中都是使用UTF-8編碼進行的,所以,若是你不寫明encoding的話,都是認爲文件是UTF-8編碼的。在Python中,好象只支持幾種編碼,象咱們經常使用的GB2312碼就不支持,所以建議你們在處理XML時使用UTF-8編碼。
2. XML文檔的結構
XML文檔有XML頭信息和XML信息體。頭信息如:
<?xml version="1.0" encoding="utf-8"?>
它代表了此XML文檔所用的版本,編碼方式。有些複雜的還有一些文檔類型的定義(DOCTYPE),用於定義此XML文檔所用的DTD或Schema和一些實體的定義。這裏並無用到,並且我也不是專家,就再也不細說了。
XML信息體是由樹狀元素組成。每一個XML文檔都有一個文檔元素,也就是樹的根元素,全部其它的元素和內容都包含在根元素中。
3. DOM
DOM是Document Object Model的簡稱,它是以對象樹來表示一個XML文檔的方法,使用它的好處就是你能夠很是靈活的在對象中進行遍歷。
4. 元素和結點
元素就是標記,它是成對出現的。XML文檔就是由元素組成的,但元素與元素之間能夠有文本,元素的內容也是文本。在minidom中有許多的結點,元素也屬於結點的一種,它不是葉子結點,即它存在子結點;還存在一些葉子結點,如文本結點,它下面再也不有子結點。
象catalog.xml中,文檔元素是catalog,它下面有兩種元素:maxid和item。maxid用來表示當前最大的item的id值。每個item都有一個id屬性,id屬性是惟一的,在 NewEdit 中用來生成每一個分類所對應的代碼片斷的XML文檔名,所以不能重複,並且它是一個遞增的值。item元素有一個caption子元素,用來表示此分類項的名稱,它還能夠包含item元素。這樣,就定義了一個樹狀XML結構,下面讓咱們看一看若是把它們讀出來。
1、獲得dom對象
>>> import xml.dom.minidom
>>> dom = xml.dom.minidom.parse('d:/catalog.xml')
這樣咱們獲得了一個dom對象,它的第一個元素應該是catalog。
2、獲得文檔元素對象
>>> root = dom.documentElement
這樣咱們獲得了根元素(catalog)。
3、結點屬性
每個結點都有它的nodeName,nodeValue,nodeType屬性。nodeName爲結點名字。
>>> root.nodeName
u'catalog'
nodeValue是結點的值,只對文本結點有效。nodeType是結點的類型,如今有如下幾種:
'ATTRIBUTE_NODE'
'CDATA_SECTION_NODE'
'COMMENT_NODE'
'DOCUMENT_FRAGMENT_NODE'
'DOCUMENT_NODE'
'DOCUMENT_TYPE_NODE'
'ELEMENT_NODE'
'ENTITY_NODE'
'ENTITY_REFERENCE_NODE'
'NOTATION_NODE'
'PROCESSING_INSTRUCTION_NODE'
'TEXT_NODE'
這些結點經過名字很好理解。catalog是ELEMENT_NODE類型。
>>> root.nodeType
1
>>> root.ELEMENT_NODE
1
4、子元素、子結點的訪問
訪問子元素、子結點的方法不少,對於知道元素名字的子元素,可使用getElementsByTagName方法,如讀取maxid子元素:
>>> root.getElementsByTagName('maxid')
[<DOM Element: maxid at 0xb6d0a8>]
這樣返回一個列表,因爲咱們的例子中maxid只有一項,所以列表也只有一項。
若是想獲得某個元素下的全部子結點(包括元素),可使用childNodes屬性:
>>> root.childNodes
[<DOM Text node "\n ">, <DOM Element: maxid at 0xb6d0a8>, <DOM Text node "\n ">, <DOM Element: item at 0xb6d918>, <DOM Text node "\n ">, <DOM Element: item at 0xb6de40>, <DOM Text node "\n ">, <DOM Element: item at 0xb6dfa8>, <DOM Text node "\n">]
能夠看出全部兩個標記間的內容都被視爲文本結點。象每行後面的回車,都被看到文本結點。從上面的結果咱們能夠看出每一個結點的類型,本例中有文本結點和元素結點;結點的名字(元素結點);結點的值(文本結點)。每一個結點都是一個對象,不一樣的結點對象有不一樣的屬性和方法,更詳細的要參見文檔。因爲本例比較簡單,只涉及文本結點和元素結點。
getElementsByTagName能夠搜索當前元素的全部子元素,包括全部層次的子元素。childNodes只保存了當前元素的第一層子結點。
這樣咱們能夠遍歷childNodes來訪問每個結點,判斷它的nodeType來獲得不一樣的內容。如,打印出全部元素的名字:
>>> for node in root.childNodes:
if node.nodeType == node.ELEMENT_NODE:
print node.nodeName
maxid
item
item
對於文本結點,想獲得它的文本內容可使用: .data屬性。
對於簡單的元素,如:<caption>Python</caption>,咱們能夠編寫這樣一個函數來獲得它的內容(這裏爲Python)。
def getTagText(root, tag):
node = root.getElementsByTagName(tag)[0]
rc = ""
for node in node.childNodes:
if node.nodeType in ( node.TEXT_NODE, node.CDATA_SECTION_NODE):
rc = rc + node.data
return rc
這個函數只處理找到的第一個符合的子元素。它會將符合的第一個子元素中的全部文本結點拼在一塊兒。當nodeType爲文本類結點時,node.data爲文本的內容。若是咱們考查一下元素caption,咱們可能看到:
[<DOM Text node "Python">]
說明caption元素只有一個文本結點。
若是一個元素有屬性,那麼可使用getAttribute方法,如:
>>> itemlist = root.getElementsByTagName('item')
>>> item = itemlist[0]
>>> item.getAttribute('id')
u'1'
這樣就獲得了第一個item元素的屬性值。
下面讓咱們簡單地小結一下如何使用minidom來讀取XML中的信息
1. 導入xml.dom.minidom模塊,生成dom對象
2. 獲得文檔對象(根對象)
3. 經過getElementsByTagName()方法和childNodes屬性(還有其它一些方法和屬性)找到要處理的元素
4. 取得元素下文本結點的內容
下面我來演示一下如何從無到有生成象catalog.xml同樣的XML文件。
1、生成dom對象
>>> import xml.dom.minidom
>>> impl = xml.dom.minidom.getDOMImplementation()
>>> dom = impl.createDocument(None, 'catalog', None)
這樣就生成了一個空的dom對象。其中catalog爲文檔元素名,即根元素名。
2、顯示生成的XML內容
每個dom結點對象(包括dom對象自己)都有輸出XML內容的方法,如:toxml(), toprettyxml()
toxml()輸出緊湊格式的XML文本,如:
<catalog><item>test</item><item>test</item></catalog>
toprettyxml()輸出美化後的XML文本,如:
<catalog>
<item>
test
</item>
<item>
test
</item>
</catalog>
能夠看出,它是將每一個結點後面都加入了回車符,而且自動處理縮近。但對於每個元素,若是元素只有文本內容,則我但願元素的tag與文本是在一塊兒的,如:
<item>test</item>
而不想是分開的格式,但minidom自己是不支持這樣的處理。關於如何實現形如:
<catalog>
<item>test</item>
<item>test</item>
</catalog>
這樣的XML格式,後面咱們再說。
3、生成各類結點對象
dom對象擁有各類生成結點的方法,下面列出文本結點,CDATA結點和元素結點的生成過程。
1. 文本結點的生成
>>> text=dom.createTextNode('test')
test
要注意的是,在生成結點時,minidom並不對文本字符進行檢查,象文本中若是出現了'<','&'之類的字符,應該轉換爲相應的實體符號'<','&'才能夠,這裏沒有作這個處理。
2. CDATA結點的生成
>>> data = dom.createCDATASection('aaaaaa\nbbbbbb')
>>> data.toxml()
'<![CDATA[aaaaaa\nbbbbbb]]>'
CDATA是用於包括大塊文本,同時能夠不用轉換'<','&'字符的標記,它是用<![CDATA[文本]]>來包括的。但文本中不能夠有"]]>"這樣的串存在。生成結點時minidom不做這些檢查,只有當你輸出時纔有可能發現有錯。
3. 元素結點的生成
>>> item = dom.createElement('caption')
>>> item.toxml()
'<caption/>'
對於象元素這樣的結點,生成的元素結點實際上是一個空元素,即不包含任何文本,若是要包含文本或其它的元素,咱們須要使用appendChild()或insertBefore()之類的方法將子結點加就到元素結點中。如將上面生成的text結點加入到caption元素結點中:
>>> item.appendChild(text)
<DOM Text node "test">
>>> item.toxml()
'<caption>test</caption>'
使用元素對象的setAttribute()方法能夠向元素中加入屬性,如:
>>> item.setAttribute('id', 'idvalue')
>>> item.toxml()
'<caption id="idvalue">test</caption>'
4、生成dom對象樹
咱們有了dom對象,又知道了如何生成各類結點,包括葉子結點(不包含其它結點的結點,如文本結點)和非葉子結點(包含其它結點的結點,如元素結點)的生成,而後就須要利用結點對象自己的appendChild()或insertBefore()方法將各個結點根據在樹中的位置連起來,串成一棵樹。最後要串到文檔結點上,即根結點上。如一個完整的示例爲:
>>> import xml.dom.minidom
>>> impl = xml.dom.minidom.getDOMImplementation()
>>> dom = impl.createDocument(None, 'catalog', None)
>>> root = dom.documentElement
>>> item = dom.createElement('item')
>>> text = dom.createTextNode('test')
>>> item.appendChild(text)
<DOM Text node "test">
>>> root.appendChild(item)
<DOM Element: item at 0xb9cf80>
>>> print root.toxml()
<catalog><item>test</item></catalog>
5、簡單生成元素結點的函數
下面是我寫的一個小函數,用於簡單的生成相似於:
<caption>test</caption>
或形如:
<item><![CDATA[test]]></item>
的元素結點
1 def makeEasyTag(dom, tagname, value, type='text'):
2 tag = dom.createElement(tagname)
3 if value.find(']]>') > -1:
4 type = 'text'
5 if type == 'text':
6 value = value.replace('&', '&')
7 value = value.replace('<', '<')
8 text = dom.createTextNode(value)
9 elif type == 'cdata':
10 text = dom.createCDATASection(value)
11 tag.appendChild(text)
12 return tag
參數說明:
函數處理說明:
所以這個小函數能夠自動地處理字符轉化及避免CDATA結點中出現']]>'串。
上面生成'item'結點的語句能夠改成:
>>> item = makeEasyTag(dom, 'item', 'test')
>>> item.toxml()
'<item>test</item>'
6、寫入到XML文件中
dom對象樹已經生成好了,咱們能夠調用dom的writexml()方法來將內容寫入文件中。writexml()方法語法格式爲:
writexml(writer, indent, addindent, newl, encoding)
writexml方法是除了writer參數必需要有外,其他能夠省略。下面給出一個文本內容有漢字的示例:
1 >>> import xml.dom.minidom
2 >>> impl = xml.dom.minidom.getDOMImplementation()
3 >>> dom = impl.createDocument(None, 'catalog', None)
4 >>> root = dom.documentElement
5 >>> text = unicode('漢字示例', 'cp936')
6 >>> item = makeEasyTag(dom, 'item', text)
7 >>> root.appendChild(item)
8 <DOM Element: item at 0xb9ceb8>
9 >>> root.toxml()
10 u'<catalog><item>\u6c49\u5b57\u793a\u4f8b</item></catalog>'
11 >>> f=file('d:/test.xml', 'w')
12 >>> import codecs
13 >>> writer = codecs.lookup('utf-8')[3](f)
14 >>> dom.writexml(writer, encoding='utf-8')
15 >>> writer.close()
5行 由於XML處理時內部使用Unicode編碼,所以象漢字首先要轉成Unicode,若是你不作這一步minicode並不檢查,而且保存時可能不會出錯。但讀取時可能會出錯。
12-13行 生成UTF-8編碼的寫入流對象,這樣在保存時會自動將Unicode轉換成UTF-8編碼。
這樣寫XML文件就完成了。
三.美化.
對於dom對象的writexml()方法,雖然能夠控制一些格式上的輸出,但結果並不讓人滿意。好比我想實現:
<catalog>
<item>test</item>
<item>test</item>
</catalog>
而不是:
<catalog>
<item>
test
</item>
<item>
test
</item>
</catalog>
若是是象下面的輸出結果我沒法區分原來文本中是否帶有空白,而上一種結果則不存在這一問題。好在我在wxPython自帶的XML資源編輯器(xred)發現了美化的代碼。代碼以下:
1 def Indent(dom, node, indent = 0):
2 # Copy child list because it will change soon
3 children = node.childNodes[:]
4 # Main node doesn't need to be indented
5 if indent:
6 text = dom.createTextNode('\n' + '\t' * indent)
7 node.parentNode.insertBefore(text, node)
8 if children:
9 # Append newline after last child, except for text nodes
10 if children[-1].nodeType == node.ELEMENT_NODE:
11 text = dom.createTextNode('\n' + '\t' * indent)
12 node.appendChild(text)
13 # Indent children which are elements
14 for n in children:
15 if n.nodeType == node.ELEMENT_NODE:
16 Indent(dom, n, indent + 1)
參數說明:
dom爲dom對象
node爲要處理的元素結點
indent指明縮近的層數
函數說明:
Indent是一個遞歸函數,當一個結點有子元素時進行遞歸處理。主要是解決子元素的換行和縮近的處理。這裏縮近是寫死的,每一級縮近使用一個製表符。若是你願意能夠改成你想要的內容。就是把函數中的'\t'換替一下。或乾脆寫成一個全局變量,或參數之後改起來可能要容易的多。不過在 NewEdit 中,這樣的處理足夠了,就沒有作這些工做。
Indent基本的想法就是遞歸遍歷全部子結點,在全部須要加入回車和縮近的地方插入相應的文本結點。這樣再使用writexml()輸出時就是縮近好了的。具體程序再也不細說,直接用就好了。
但這裏要注意的是:
Indent()要修改原dom對象,所以在調用它以前最好先複製一個臨時dom對象,使用完畢後再清除這個臨時dom對象便可。下面是詳細的調用過程:
1 domcopy = dom.cloneNode(True)
2 Indent(domcopy, domcopy.documentElement)
3 f = file(xmlfile, 'wb')
4 writer = codecs.lookup('utf-8')[3](f)
5 domcopy.writexml(writer, encoding = 'utf-8')
6 domcopy.unlink()
1行 克隆一個dom對象
2行 進行縮近處理
3-4行 進行UTF-8編碼處理
5行 生成XML文件
6行 清除dom對象的內容
通過這番處理以後,你的XML文檔應該好看多了。