Создать структуру в плоском XML-файле

1

У меня есть xml файл, сделанный вот так:

<car>Ferrari</car>
<color>red</color>
<speed>300</speed>
<car>Porsche</car>
<color>black</color>
<speed>310</speed>

Мне нужно иметь его в этой форме:

<car name="Ferrari">
    <color>red</color>
    <speed>300</speed>
</car>
<car name="Porsche">
    <color>black</color>
    <speed>310</speed>
</car>

Как я могу это сделать? Я боюсь, потому что не могу придумать способ создания структуры, которая мне нужна, из плоского lis тегов в исходном xml файле.

Мой язык выбора - Python, но любое предложение приветствуется.

  • 1
    Размещенный вами «XML» не является допустимым XML. У него нет корневого элемента или объявления XML вверху.
  • 0
    @Oded: объявление XML на самом деле не является обязательным.
Показать ещё 1 комментарий
Теги:

4 ответа

1
Лучший ответ

Я не знаю о python, но, полагая, что у вас есть XML-парсер, который дал вам иерархический доступ к узлам в XML-документе, семантика, которую вы хотите, будет примерно такой: (предупреждение, я склонен использовать PHP). В основном, сохраняйте теги без "автомобиля", а затем, когда вы сталкиваетесь с новым тегом "автомобиль", относитесь к нему как к разделительному полю и создавайте собранный XML node:

// Create an input and output handle
input_handle = parse_xml_document();
output_handle = new_xml_document();

// Assuming the <car>, <color> etc. nodes are
// the children of some, get them as an array
list_of_nodes = input_handle.get_list_child_nodes();

// These are empty variables for storing our data as we parse it
var car, color, speed = NULL

foreach(list_of_nodes as node)
{
  if(node.tag_name() == "speed")
  {
    speed = node.value();
    // etc for each type of non-delimiting field          
  }

  if(node.tag_name() == "car")
  {
    // If there already a car specified, take its data,
    // insert it into the output xml structure and th
    if(car != NULL)
    {
      // Add a new child node to the output document
      node = output_handle.append_child_node("car");
      // Set the attribute on this new output node
      node.set_attribute("name", node.value());
      // Add the stored child attributes
      node.add_child("color", color);
      node.add_child("speed", speed);
    }

    // Replace the value of car afterwards. This allows the
    // first iteration to happen when there is no stored value
    // for "car".
    car = node.value();

  }
}
  • 0
    Очень полезный совет, моя настоящая проблема была, конечно, более сложной, чем та, которую я написал, но, исходя из ваших ответов, я нашел решение. Благодарю.
7

XSLT - идеальный инструмент для преобразования одной структуры XML в другую.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <!-- copy the root element and handle its <car> children -->
  <xsl:template match="/root">
    <xsl:copy>
      <xsl:apply-templates select="car" />
    <xsl:copy>
  </xsl:template>

  <!-- car elements become a container for their properties -->
  <xsl:template match="car">
    <car name="{normalize-space()}">
      <!-- ** see 1) -->
      <xsl:copy-of select="following-sibling::color[1]" />
      <xsl:copy-of select="following-sibling::speed[1]" />
    </car>
  </xsl:template>
</xsl:stylesheet>

1) Чтобы это работало, ваш XML должен иметь <color> и <speed> для каждого <car>. Если это не гарантировано, или количество и вид свойств обычно являются переменными, замените две строки на общую форму оператора копирования:

<!-- any following-sibling element that "belongs" to the same <car> -->
<xsl:copy-of select="following-sibling::*[
  generate-id(preceding-sibling::car[1]) = generate-id(current())
]" />

Применительно к вашему XML (я подразумевал элемент документа с именем <root>), это было бы результатом

<root>
  <car name="Ferrari">
    <color>red</color>
    <speed>300</speed>
  </car>
  <car name="Porsche">
    <color>black</color>
    <speed>310</speed>
  </car>
</root>

Пример кода, который применяет XSLT к XML в Python, должен быть очень легко найти, поэтому я опускаю это здесь. Это будет чуть больше четырех или пяти строк кода Python.

  • 1
    +1: самое интересное. Я собрал решение, которое итерировало детей root в 3 с небольшой проверкой ошибок. Очень полезно увидеть XSLT, чтобы получить тот же результат "правильным способом". Спасибо.
  • 0
    Спасибо за предложение, к сожалению, я не разбираюсь в xslt и не могу адаптировать ваше решение к реальному случаю.
Показать ещё 1 комментарий
0

Предполагая, что первым элементом внутри корня является элемент car, и все элементы не car "принадлежат" последнему car:

import xml.etree.cElementTree as etree

root = etree.XML('''<root>
<car>Ferrari</car>
<color>red</color>
<speed>300</speed>
<car>Porsche</car>
<color>black</color>
<speed>310</speed>
</root>''')

new_root = etree.Element('root')

for elem in root:
    if elem.tag == 'car':
        car = etree.SubElement(new_root, 'car', name=elem.text)
    else:
        car.append(elem)

new_root будет:

<root><car name="Ferrari"><color>red</color>
<speed>300</speed>
</car><car name="Porsche"><color>black</color>
<speed>310</speed>
</car></root>

(Я предположил, что симпатичные пробелы не важны)

0

IF данные вашей реальной жизни просты, как ваш пример, и в нем нет ошибок, вы можете использовать замещение регулярного выражения, чтобы сделать это за один удар:

import re

guff = """
<car>Ferrari</car>
<color>red</color>
<speed>300</speed>
<car>Porsche</car>
<color>black</color>
<speed>310</speed>
"""

pattern = r"""
<car>([^<]+)</car>\s*
<color>([^<]+)</color>\s*
<speed>([^<]+)</speed>\s*
"""

repl = r"""<car name="\1">
    <color>\2</color>
    <speed>\3</speed>
</car>
"""

regex = re.compile(pattern, re.VERBOSE)
output = regex.sub(repl, guff)
print output

В противном случае вам лучше прочитать его по 3 строки за раз, выполнить некоторые проверки и записать один элемент "автомобиль" за раз, используя строчную обработку или ElementTree.

  • 3
    WTF ?! Вы рекомендуете регулярное выражение для обработки XML? Тебе должно быть стыдно. Серьезно, это паршивый подход на всех мыслимых уровнях.
  • 2
    Прочитайте первую строку моего ответа еще раз. Это было условное «вы можете», а не рекомендация. В любом случае, называть его ввод «XML» - это очень благотворительный акт.
Показать ещё 5 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню