调用lxml.etree.canonicalize触发命名空间未声明ValueError问题
解决lxml canonicalize处理Envelope子元素Header时的命名空间错误
调用lxml.etree.canonicalize()处理作为Envelope子元素的Header节点时,会抛出如下ValueError异常:
ValueError: Namespace "http://schemas.xmlsoap.org/soap/envelope/" of name "Header" is not declared in scope
现象对比
单独创建Header节点时正常:直接构建Header节点并调用
canonicalize能返回预期结果:from lxml import etree as ET url_soap_envelope = "http://schemas.xmlsoap.org/soap/envelope/" # 单独创建Header节点 nsmap_Header = { 's': url_soap_envelope, } qname_s_Header = ET.QName(url_soap_envelope, "Header") node_header = ET.Element(qname_s_Header, nsmap=nsmap_Header) ET.canonicalize(node_header) # 返回结果: '<s:Header xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"></s:Header>'作为Envelope子元素时报错:当Header是以下XML模板中Envelope的子元素时,调用
ET.canonicalize(node_header)触发错误,预期结果应为'<s:Header xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"></s:Header>':<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <s:Header> <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <u:Timestamp u:Id="_0"> <u:Created>{created} </u:Created> <u:Expires>{expires} </u:Expires> </u:Timestamp> <o:BinarySecurityToken u:Id="{uuid}" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">{b64certificate} </o:BinarySecurityToken> <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> <SignedInfo> <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> <Reference URI="#_0"> <Transforms> <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <DigestValue>{digest_value} </DigestValue> </Reference> </SignedInfo> <SignatureValue>{b64signature} </SignatureValue> <KeyInfo> <o:SecurityTokenReference> <o:Reference ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" URI="#{uuid}"/> </o:SecurityTokenReference> </KeyInfo> </Signature> </o:Security> </s:Header> <s:Body> <Autentica xmlns="http://DescargaMasivaTerceros.gob.mx"/> </s:Body> </s:Envelope>
问题原因
Header作为Envelope的子元素时,其命名空间声明是继承自父节点的。单独抽取Header节点进行canonicalize时,lxml无法自动携带父节点的命名空间上下文,导致节点自身没有显式的命名空间声明,触发作用域未声明的错误。
解决方案
方案1:显式为Header节点添加命名空间声明
获取Header节点后,手动为其添加s前缀的命名空间声明,确保节点自身包含完整的命名空间信息:
# 假设已构建好Envelope节点envelope node_header = envelope.find('{http://schemas.xmlsoap.org/soap/envelope/}Header') # 显式添加命名空间声明 node_header.set('xmlns:s', 'http://schemas.xmlsoap.org/soap/envelope/') # 调用canonicalize即可正常执行 result = ET.canonicalize(node_header)
方案2:保留根节点上下文进行canonicalize
通过获取Header节点的根树来保留完整的命名空间作用域,再进行canonicalize操作:
# 获取Header节点所属的根树 root_tree = node_header.getroottree() # 对根树执行canonicalize,若仅需Header部分可后续提取 result = ET.canonicalize(root_tree)
内容的提问来源于stack exchange,提问作者Adan Cortes
相关产品推荐
相关产品推荐

