Удалите специальные символы из ключей анализируемого XML-файла, используя xmltodict

Я проанализировал XML-файл, используя xmltodict Модуль и результат хранятся в словаре словарей.

Теперь я хочу удалить специальные символы @ а также # в каждом ключе словаря.

def remove_using_json(parse_result):
    data = {}
    data = json.dumps(parse_result)
    #print data
    #for d in data:
    for key, value in data.iterkeys():
        if key[0] == '@':
            data[key]=key.strip("@")
        elif key[0] == '#':
            data[key] =key.strip("#")

3 ответа

Вы не должны удалять эти специальные символы из вашего ответа.

Есть вариант вообще не включать их в свой ответ.;-)

result = xmltodict.parse(response, attr_prefix='@', cdata_key='#text')

Это параметры по умолчанию, но вы можете установить attr_prefix='' избавиться от @ символы и изменения cdata_key таким же образом.

PS Кроме того, вы также можете использовать dict_constructor=dict например, для создания словарей в разобранном ответе вместо OrderDicts, если вы не хотите конвертировать его обратно в XML с помощью xmltodict.unparse(),

Удалять @ из ключей использования словаря attr_prefix='' в качестве аргумента функции xmltodict.parse(). Удалять # из ключей использования словаря cdata_key='text' в качестве аргумента xmltodict.parse() функция.

Текстовые значения для узлов могут быть указаны с помощью cdata_key введите python dict, а свойства узла можно указать с помощью attr_prefix с префиксом имени ключа в python dict. Значение по умолчанию для attr_prefix является @ и значение по умолчанию для cdata_key является #text,

Нажмите здесь для подробностей.

Не существует прямого способа исключить их во время синтаксического анализа, поскольку они используются для обозначения атрибутов и текстовых узлов, позволяющих их отличать от элементов (если бы их не было, выходные данные были бы непригодны для использования).

Например

xmltodict.parse("""
    <root>
        <abc><def>ab</def></abc>
        <abc id="a3">efg</abc>
    </root>
""")

производит вложенный упорядоченный dict со структурой

{'root': {'abc': [ 
                     {'def': 'ab'},
                     {'@id': 'a3', '#text': 'efg'}
                 ]
         }
}

Символ @ говорит мне, что @id является атрибутом. Без этого символа я не мог бы сказать, был ли это атрибут или элемент с именем id. Точно так же символ # говорит мне, что #text - это текстовое значение этого элемента. Без этого я не мог бы сказать, был ли это текст элемента, или это был элемент с именем text.

Однако при работе с ключами их можно раздеть, используя ky[1:] где ky это ключ.

Например, если я назначу вышеупомянутый анализируемый вывод переменной doc Я могу сделать 1

for abcelem in doc["root"]["abc"]:
    for ky in abcelem:
        if ky[0]=="@": print("Attribute:",ky[1:])
        elif ky[0]=="#": print("Text Content")
        else: print("Element:",ky)

Который будет выводить

Element: def
Attribute: id
Text Content

где я убрал символ @ из атрибута.


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

def remover(x):
    if isinstance(x,list): return [remover(y) for y in x]
    elif isinstance(x,OrderedDict):
        for ky in list(x.keys()):
            if ky[0] in ["@","#"]: 
                x[ky[1:]] = remover(x[ky])
                del x[ky]
            else: x[ky] = remover(x[ky])
        return x
    else: return x

Таким образом, в приведенном выше remover(doc) удалит все символы @ и # из ключей. Поведение может быть нестабильным и потерять некоторые данные, если какой-либо узел имеет элемент и атрибут с тем же именем или элемент или атрибут с именем text, и именно поэтому эти символы присутствуют в первую очередь. Эта функция изменяет объект на месте, и, таким образом, если необходимо сохранить оригинал, необходимо сделать глубокую копию и передать функции.


1 Используется синтаксис Python 3, где команда печати является функцией. Чтобы этот пример работал в Python 2.6 или 2.7, сначала выпустите from __future__ import print_function или измените вызовы функции print на операторы типа print "Attribute: "+ky[1:],

Это происходит потому, что ваша функция не углубляется. Итак, давайте возьмем примерный ответ из ответа @Matthew, например:

d = xmltodict.parse("""
    <root>
        <abc><def>ab</def></abc>
        <abc id="a3">efg</abc>
    </root>
""")

In [29]: d
Out[29]: {'root': {'abc': [{'def': 'ab'}, {'#text': 'efg', '@id': 'a3'}]}}

Ваша функция найдет только один ключ в этой записи: root, Но вы можете рекурсивно перебирать все элементы так:

# What if you use different from dict Mapping implementation
# ...like OrderedDict or defaultdict? So lets check type 
# ...of the nested 'dicts' with Mapping interface
from collections import Mapping

def transform(element, strip_chars="#@"):
    if isinstance(element, Mapping):
        return {key.strip(strip_chars): transform(value) 
                    for key, value 
                    in element.iteritems()}
    elif isinstance(element, list):
        return [transform(item) for item in element]
    else:
        return element

In [27]: d1 = transform(d)

In [28]: d, d1
Out[28]: 
({'root': {'abc': [{'def': 'ab'}, {'#text': 'efg', '@id': 'a3'}]}},
 {'root': {'abc': [{'def': 'ab'}, {'id': 'a3', 'text': 'efg'}]}})
Другие вопросы по тегам