我正在使用 Python 开发一个 API,它使用 XML 进行服务器调用。我正在讨论是否使用库(例如http://wiki.python.org/moin/MiniDom)或者使用字符串连接来生成是否“更好”(意味着更少的开销和更快)用于每个请求的 XML。此外,我将生成的 XML 将是非常动态的,所以我不确定允许我动态管理元素的东西是否会带来好处。
5 回答
既然您只是在使用 authorize.net,为什么不使用专门为 Authorize.net API 设计的库而忘记构建自己的 XML 调用呢?
如果您想要或需要使用自己的方式使用 XML,请不要使用 minidom,而是使用带有ElementTree
接口的东西,例如cElementTree
(在标准库中)。它会少很多痛苦,可能会快得多。您肯定需要一个 XML 库来解析您生成的 XML,因此您不妨为两者使用相同的 API。
使用 XML 库的开销不太可能成为问题,而且干净代码和知道您不能生成无效 XML 的好处非常大。
如果您绝对肯定需要尽可能快,请使用可用于 Python的极快模板库之一。它们可能比你做的任何简单的字符串连接都要快得多,而且也是安全的(即进行适当的转义)。
另一种选择是使用Jinja,尤其是当您的 xml 的动态特性相当简单时。这是烧瓶中用于生成 html 响应的常见习语。
以下是生成 aws S3列表对象响应的 XML 的 jinja 模板示例。我通常将模板存储在一个单独的文件中,以避免用丑陋的 xml 污染我优雅的 python。
from datetime import datetime
from jinja2 import Template
list_bucket_result = """<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>{{bucket_name}}</Name>
<Prefix/>
<KeyCount>{{object_count}}</KeyCount>
<MaxKeys>{{max_keys}}</MaxKeys>
<IsTruncated>{{is_truncated}}</IsTruncated>
{%- for object in object_list %}
<Contents>
<Key>{{object.key}}</Key>
<LastModified>{{object.last_modified_date.isoformat()}}</LastModified>
<ETag></ETag>
<Size>{{object.size}}</Size>
<StorageClass>STANDARD</StorageClass>
</Contents>{% endfor %}
</ListBucketResult>
"""
class BucketObject:
def __init__(self, key, last_modified_date, size):
self.key = key
self.last_modified_date = last_modified_date
self.size = size
object_list = [
BucketObject('/foo/bar.txt', datetime.utcnow(), 10*1024 ),
BucketObject('/foo/baz.txt', datetime.utcnow(), 29*1024 ),
]
template = Template(list_bucket_result)
result = template.render(
bucket_name='test-bucket',
object_count=len(object_list),
max_keys=1000,
is_truncated=False,
object_list=object_list
)
print result
输出:
<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>test-bucket</Name>
<Prefix/>
<KeyCount>2</KeyCount>
<MaxKeys>1000</MaxKeys>
<IsTruncated>False</IsTruncated>
<Contents>
<Key>/foo/bar.txt</Key>
<LastModified>2017-10-31T02:28:34.551000</LastModified>
<ETag></ETag>
<Size>10240</Size>
<StorageClass>STANDARD</StorageClass>
</Contents>
<Contents>
<Key>/foo/baz.txt</Key>
<LastModified>2017-10-31T02:28:34.551000</LastModified>
<ETag></ETag>
<Size>29696</Size>
<StorageClass>STANDARD</StorageClass>
</Contents>
</ListBucketResult>
我肯定会建议您使用其中一个 Python 库;例如MiniDom、ElementTree、lxml.etree或pyxser。没有理由不这样做,并且潜在的性能影响将是最小的。
虽然,我个人更喜欢使用simplejson(或简单的json)。
my_list = ["Value1", "Value2"]
json = simplejson.dumps(my_list)
# send json
我真正的问题是你想要完成的最大问题是什么?如果您担心速度/内存,那么是的,minidom 确实会受到打击。如果您想要可以快速部署的相当可靠的东西,我会说使用它。
对于用任何语言(Java、Python、C#、Perl 等)处理 XML,我的建议是考虑使用已经存在的东西。每个人都至少编写过一次自己的 XML 解析器,然后他们再也不会这样做了,因为这在背后太痛苦了。公平地说,这些库已经解决了您遇到的任何问题的 99.5%。
我推荐LXML。它是一个 Python 绑定库,用于非常快速的 C 库libxml2和libxslt。
LXML 支持 XPATH,并且有一个elementTree实现。LXML 还有一个名为objectify的接口,用于将 XML 编写为对象层次结构:
from lxml import etree, objectify
E = objectify.ElementMaker(annotate=False)
my_alpha = my_alpha = E.alpha(E.beta(E.gamma(firstattr='True')),
E.beta(E.delta('text here')))
etree.tostring(my_alpha)
# '<alpha><beta><gamma firstattr="True"/></beta><beta><delta>text here</delta></beta></alpha>'
etree.tostring(my_alpha.beta[0])
# '<beta><gamma firstattr="True"/></beta>'
my_alpha.beta[1].delta.text
# 'text here'