Генерация XML в Python

Я разрабатываю API с использованием Python, который выполняет вызовы сервера с использованием XML. Я спорю о том, использовать ли библиотеку (например, http://wiki.python.org/moin/MiniDom) или было бы "лучше" (имеется в виду меньше накладных расходов и быстрее) использовать конкатенацию строк для генерации XML используется для каждого запроса. Кроме того, XML, который я буду генерировать, будет довольно динамичным, поэтому я не уверен, что что-то, что позволит мне динамически управлять элементами, принесет пользу.

5 ответов

Решение

Я определенно рекомендую вам использовать одну из библиотек Python; такие как MiniDom, ElementTree, lxml.etree или pyxser. Нет причин не делать этого, и потенциальное влияние на производительность будет минимальным.

Хотя лично я предпочитаю использовать simplejson (или просто json).

my_list = ["Value1", "Value2"]
json = simplejson.dumps(my_list)
# send json

Другой вариант - использовать Jinja, особенно если динамическая природа вашего xml довольно проста. Это обычная идиома в колбе для генерации HTML-ответов.

Вот пример шаблона jinja, который генерирует XML ответа объекта списка A3 S3. Я обычно храню шаблон в отдельном файле, чтобы избежать загрязнения моего элегантного питона уродливым xml.

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>

Поскольку вы просто используете authorize.net, почему бы не использовать библиотеку, специально разработанную для API Authorize.net, и забыть о создании собственных вызовов XML?

Если вы хотите или должны идти своим путем с XML, не используйте minidom, используйте что-то с ElementTree интерфейс, такой как cElementTree (который находится в стандартной библиотеке). Это будет намного менее болезненным и, вероятно, намного быстрее. Вам, безусловно, понадобится библиотека XML для анализа созданного вами XML, так что вы могли бы также использовать один и тот же API для обоих.

Маловероятно, что использование библиотеки XML станет проблемой, а польза от чистого кода и знания того, что вы не можете сгенерировать недопустимый XML, очень велики.

Если вам абсолютно необходимо быть максимально быстрым, используйте одну из чрезвычайно быстрых библиотек шаблонов, доступных для Python. Вероятно, они будут намного быстрее, чем любая наивная конкатенация строк, которую вы делаете, и также будут безопасны (то есть будут выполнять правильное экранирование).

Мой реальный вопрос: что больше всего беспокоит то, чего вы пытаетесь достичь? Если вы беспокоитесь о скорости / памяти, тогда да, минидом действительно получает удар. Если вы хотите что-то достаточно надежное и быстро развернуть, я бы сказал, используйте это.

Мое предложение по работе с XML на любом языке (Java, Python, C#, Perl и т. Д.) - рассмотреть возможность использования чего-то уже существующего. Каждый хотя бы один раз написал свой собственный синтаксический анализатор 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'
Другие вопросы по тегам