У меня есть 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, но любое предложение приветствуется.
Я не знаю о 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();
}
}
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.
Предполагая, что первым элементом внутри корня является элемент 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>
(Я предположил, что симпатичные пробелы не важны)
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.