簡體   English   中英

Python UTF-8 XML解析(SUDS):刪除“無效令牌”

[英]Python UTF-8 XML parsing (SUDS): Removing 'invalid token'

處理UTF-8時出現常見錯誤 - “無效令牌”

在我的例子中,它來自處理不尊重unicode字符的SOAP服務提供者,只是將值截斷為100字節而忽略了第100個字節可能在多字節字符的中間:例如:

<name xsi:type="xsd:string">浙江家庭教會五十人遭驅散及抓打 聖誕節聚會被斷電及搶走物品(圖、視頻\xef\xbc</name>

在截斷刀假定世界使用1字節字符之后,最后兩個字節是3字節unicode字符的剩余字節。 下一站,sax解析器和:

xml.sax._exceptions.SAXParseException: <unknown>:1:2392: not well-formed (invalid token)

我不再關心這個角色了。 它應該從文檔中刪除並允許sax解析器運行。

除了這些值之外,XML回復在其他方面都有效。

問題:如何在不解析整個文檔並重新發明UTF-8編碼來檢查每個字節的情況下如何刪除此字符?

使用:Python + SUDS

事實證明,SUDS將xml看作類型'string'(不是unicode),因此這些是編碼值。

1)過濾器:

badXML = "your bad utf-8 xml here"  #(type <str>)

#Turn it into a python unicode string - ignore errors, kick out bad unicode
decoded = badXML.decode('utf-8', errors='ignore')  #(type <unicode>)

#turn it back into a string, using utf-8 encoding.
goodXML = decoded.encode('utf-8')   #(type <str>)

2)SUDS:請參閱https://fedorahosted.org/suds/wiki/Documentation#MessagePlugin

from suds.plugin import MessagePlugin
class UnicodeFilter(MessagePlugin):
    def received(self, context):
        decoded = context.reply.decode('utf-8', errors='ignore')
        reencoded = decoded.encode('utf-8')
        context.reply = reencoded

from suds.client import Client
client = Client(WSDL_url, plugins=[UnicodeFilter()])

希望這有助於某人。


注意:感謝John Machin

請參閱: 為什么python decode會替換編碼字符串中的無效字節?

關於errors='ignore' Python issue8271可能會妨礙你。 如果沒有在python中修復此錯誤,'ignore'將使用接下來的幾個字節來滿足長度

在解碼無效的UTF-8字節序列期間,只有
起始字節和連續字節現在被認為是無效的,而不是起始字節指定的字節數

問題修復於:
Python 2.6.6 rc1
Python 2.7.1 rc1(以及2.7的所有未來版本)
Python 3.1.3 rc1(以及3.x的所有未來版本)

Python 2.5及更低版本將包含此問題。

在上面的例子中, "\\xef\\xbc</name".decode('utf-8', errors='ignore')應該
返回"</name" ,但在'bugged'版本的python中它返回"/name"

前四位( 0xe )描述了一個3字節的UTF字符,因此0xbc了字節0xef0xbc ,然后(錯誤地) 0x3c'<' )。

0x3c不是一個有效的連續字節,它首先創建無效的3字節UTF字符。

固定版本的python只刪除第一個字節,只刪除有效的連續字節,而不使用0x3c

@ FlipMcF是正確的答案 - 我只是為他的解決方案發布了我的過濾器,因為原來的那個沒有為我工作(我的XML中有一些表情符號字符,它們是用UTF-8正確編碼的,但它們是仍然崩潰的XML解析器):

class UnicodeFilter(MessagePlugin):
    def received(self, context):
        from lxml import etree
        from StringIO import StringIO
        parser = etree.XMLParser(recover=True) # recover=True is important here
        doc = etree.parse(StringIO(context.reply), parser)
        context.reply = etree.tostring(doc)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM