У меня есть большое количество XML файлов, в которых я хочу внести следующие изменения:
Создайте новый элемент - позвольте ему new_element
- под корневым элементом
Найдите другой, специфический элемент, который более глубоко вложен - позвоните ему в existing_element
- и переместите его так, чтобы он стал дочерним элементом new_element
Я хочу сделать это наименее навязчивым способом, так что разница между старым файлом и новым файлом показывает только изменения в строках, которые либо принадлежат вновь созданному элементу (и, следовательно, были добавлены в файл), либо используются принадлежать элементу, который был перемещен (и, следовательно, удален из файла). Я хочу сделать это с помощью Python 3.
Тем не менее, когда я пытаюсь просто прочитать один из файлов XML с помощью xml.dom.minidom
и написать то, что я только что прочитал в новом файле, и разделить два файла, каждая строка будет отмечена как измененная (вероятно, потому, что они содержат разные типы новых строк), Кроме того, когда я просматриваю содержимое этих двух файлов, я вижу, что спецификация кодирования в объявлении XML, а также новая строка после объявления исчезли, а атрибуты в тегах по всему документу имели порядок, перетасованный внутри тег.
История очень похожа при использовании xml.etree.ElementTree
, только теперь исчезает вся декларация XML, всем именам меток предшествует " ns0:
" (по какой-то причине), а за некоторыми именами атрибутов следует " :ns0
".
Ни одна из этих "лишних" модификаций в XML файлах не является желательной, поскольку я хочу иметь возможность различать старый файл и новый файл и иметь возможность легко видеть, что изменилось, а что нет.
Итак, есть ли простой способ создать новый XML файл на основе другого XML файла, который только вводит изменения в строки, которые действительно должны быть изменены, и оставить все остальные строки нетронутыми, и это не связано с написанием собственного кода для синтаксического анализа данные XML?
Редактировать: Вот структура файла, который я хочу обработать (поскольку я не знаю, что вызовет какой-либо предлагаемый код для работы или не работать с моими XML файлами, я только удалил данные, хранящиеся в трех разных местах в файл - который я заменил на " *data*
" - и сохранил все остальное точно так, как есть):
<?xml version="1.0" encoding="UTF-8"?>
<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
<asset>
<contributor/>
<created>2017-01-23T12:01:30Z</created>
<modified>2017-01-23T12:01:30Z</modified>
<unit/>
<up_axis>Z_UP</up_axis>
</asset>
<library_visual_scenes>
<visual_scene id="defaultScene">
<node id="sceneRoot">
<instance_geometry url="#geometry">
<bind_material>
<technique_common>
<instance_material symbol="geometry_material" target="#material">
<bind_vertex_input semantic="texcoord0" input_semantic="TEXCOORD" input_set="0"/>
</instance_material>
</technique_common>
</bind_material>
</instance_geometry>
</node>
</visual_scene>
</library_visual_scenes>
<library_geometries>
<geometry id="geometry">
<mesh>
<source id="geometry-positions">
<float_array id="geometry-positions-array" count="673731">*data*</float_array>
<technique_common>
<accessor count="224577" source="#geometry-positions-array" stride="3">
<param name="X" type="float"/>
<param name="Y" type="float"/>
<param name="Z" type="float"/>
</accessor>
</technique_common>
</source>
<source id="geometry-texcoord_0">
<float_array id="geometry-texcoord_0-array" count="449154">*data*</float_array>
<technique_common>
<accessor count="224577" source="#geometry-texcoord_0-array" stride="2">
<param name="S" type="float"/>
<param name="T" type="float"/>
</accessor>
</technique_common>
</source>
<vertices id="geometry-vertices">
<input semantic="POSITION" source="#geometry-positions"/>
</vertices>
<triangles count="329753" material="geometry_material">
<input offset="0" semantic="VERTEX" source="#geometry-vertices" set="0"/>
<input offset="1" semantic="TEXCOORD" source="#geometry-texcoord_0" set="0"/>
<p>*data*</p>
</triangles>
</mesh>
</geometry>
</library_geometries>
<library_materials>
<material id="material">
<instance_effect url="#material_effect"/>
</material>
</library_materials>
<library_effects>
<effect id="material_effect">
<profile_COMMON>
<image id="material_effect-image" height="0" width="0">
<init_from>Tile_+037_+047_0.jpg</init_from>
</image>
<newparam sid="material_effect-surface">
<surface type="2D">
<init_from>material_effect-image</init_from>
</surface>
</newparam>
<newparam sid="material_effect-sampler">
<sampler2D>
<source>material_effect-surface</source>
<wrap_s>CLAMP</wrap_s>
<wrap_t>CLAMP</wrap_t>
<minfilter>LINEAR_MIPMAP_LINEAR</minfilter>
<magfilter>LINEAR</magfilter>
<border_color>0 0 0 0</border_color>
</sampler2D>
</newparam>
<technique sid="t0">
<phong>
<emission>
<color>0 0 0 1</color>
</emission>
<ambient>
<color>1 1 1 1</color>
</ambient>
<diffuse>
<texture texture="material_effect-sampler" texcoord="texcoord0">
<extra type="color">
<technique profile="SCEI">
<color>1 1 1 1</color>
</technique>
</extra>
</texture>
</diffuse>
<specular>
<color>0 0 0 1</color>
</specular>
<shininess>
<float>0</float>
</shininess>
</phong>
</technique>
</profile_COMMON>
</effect>
</library_effects>
<scene>
<instance_visual_scene url="#defaultScene"/>
</scene>
</COLLADA>
Это то, что я хочу, чтобы XML превратился в:
<?xml version="1.0" encoding="UTF-8"?>
<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
<asset>
<contributor/>
<created>2017-01-23T12:01:30Z</created>
<modified>2017-01-23T12:01:30Z</modified>
<unit/>
<up_axis>Z_UP</up_axis>
</asset>
<library_visual_scenes>
<visual_scene id="defaultScene">
<node id="sceneRoot">
<instance_geometry url="#geometry">
<bind_material>
<technique_common>
<instance_material symbol="geometry_material" target="#material">
<bind_vertex_input semantic="texcoord0" input_semantic="TEXCOORD" input_set="0"/>
</instance_material>
</technique_common>
</bind_material>
</instance_geometry>
</node>
</visual_scene>
</library_visual_scenes>
<library_geometries>
<geometry id="geometry">
<mesh>
<source id="geometry-positions">
<float_array id="geometry-positions-array" count="673731">*data*</float_array>
<technique_common>
<accessor count="224577" source="#geometry-positions-array" stride="3">
<param name="X" type="float"/>
<param name="Y" type="float"/>
<param name="Z" type="float"/>
</accessor>
</technique_common>
</source>
<source id="geometry-texcoord_0">
<float_array id="geometry-texcoord_0-array" count="449154">*data*</float_array>
<technique_common>
<accessor count="224577" source="#geometry-texcoord_0-array" stride="2">
<param name="S" type="float"/>
<param name="T" type="float"/>
</accessor>
</technique_common>
</source>
<vertices id="geometry-vertices">
<input semantic="POSITION" source="#geometry-positions"/>
</vertices>
<triangles count="329753" material="geometry_material">
<input offset="0" semantic="VERTEX" source="#geometry-vertices" set="0"/>
<input offset="1" semantic="TEXCOORD" source="#geometry-texcoord_0" set="0"/>
<p>*data*</p>
</triangles>
</mesh>
</geometry>
</library_geometries>
<library_materials>
<material id="material">
<instance_effect url="#material_effect"/>
</material>
</library_materials>
<library_effects>
<effect id="material_effect">
<profile_COMMON>
<newparam sid="material_effect-surface">
<surface type="2D">
<init_from>material_effect-image</init_from>
</surface>
</newparam>
<newparam sid="material_effect-sampler">
<sampler2D>
<source>material_effect-surface</source>
<wrap_s>CLAMP</wrap_s>
<wrap_t>CLAMP</wrap_t>
<minfilter>LINEAR_MIPMAP_LINEAR</minfilter>
<magfilter>LINEAR</magfilter>
<border_color>0 0 0 0</border_color>
</sampler2D>
</newparam>
<technique sid="t0">
<phong>
<emission>
<color>0 0 0 1</color>
</emission>
<ambient>
<color>1 1 1 1</color>
</ambient>
<diffuse>
<texture texture="material_effect-sampler" texcoord="texcoord0">
<extra type="color">
<technique profile="SCEI">
<color>1 1 1 1</color>
</technique>
</extra>
</texture>
</diffuse>
<specular>
<color>0 0 0 1</color>
</specular>
<shininess>
<float>0</float>
</shininess>
</phong>
</technique>
</profile_COMMON>
</effect>
</library_effects>
<scene>
<instance_visual_scene url="#defaultScene"/>
</scene>
<library_images>
<image id="material_effect-image" height="0" width="0">
<init_from>Tile_+037_+047_0.jpg</init_from>
</image>
</library_images>
</COLLADA>
Обратите внимание, что во втором фрагменте XML в корневом элементе был создан тег с именем library_images
(где под корневым элементом не важно, если он является прямым потомком его), и image
элемента было перемещено в Это.
Да, существует способ, называемый XSLT, язык специального назначения, предназначенный для преобразования XML файлов из одной структуры в другую или других форматов, включая HTML, TXT/CSV, даже JSON. Модуль сторонних разработчиков Python, lxml
может запускать скрипты XSLT 1.0 (не встроенные модули minidom
или etree
). Однако другие языки (Java, С#, PHP, VB) и программные средства (Saxon, Xalan, libxslt,.NET) также могут запускать такие скрипты даже для сценариев 2.0 и 3.0. И Python может подключаться к этим внешним решениям через вызовы командной строки.
В частности, запустите Identity Transform, чтобы сохранить исходный формат как есть, а затем примените необходимые изменения к определенным узлам. Одна из проблем заключается в обработке пространства имен по умолчанию, требующего определения префикса, документа и namespace
при создании нового элемента, библиотечных изображений:
XSLT (сохранить как.xsl)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:doc="http://www.collada.org/2005/11/COLLADASchema">
<xsl:output omit-xml-declaration="no" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- IDENTITY TRANSFORM -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- ADD <new_element> AS CHILD TOO ROOT -->
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
<xsl:element name="library-images" namespace="http://www.collada.org/2005/11/COLLADASchema">
<xsl:copy-of select="descendant::doc:profile_COMMON/doc:image"/>
</xsl:element>
</xsl:copy>
</xsl:template>
<!-- REMOVE NODE IN DOCUMENT -->
<xsl:template match="doc:profile_COMMON/doc:image"/>
</xsl:stylesheet>
питон
import lxml.etree as et
# LOAD XML AND XSL
doc = et.parse('my_file.xml')
xsl = et.parse('my_script.xsl')
# CONFIGURE TRANSFORMER
transform = et.XSLT(xsl)
# RUN TRANSFORMATION WITH PARAMS
result = transform(doc)
# PRINT RESULT
print(result)
# SAVE TO FILE
with open('output.xml', 'wb') as f:
f.write(result)
Выход
<?xml version="1.0" encoding="UTF-8"?>
<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
<asset>
<contributor/>
<created>2017-01-23T12:01:30Z</created>
<modified>2017-01-23T12:01:30Z</modified>
<unit/>
<up_axis>Z_UP</up_axis>
</asset>
<library_visual_scenes>
<visual_scene id="defaultScene">
<node id="sceneRoot">
<instance_geometry url="#geometry">
<bind_material>
<technique_common>
<instance_material symbol="geometry_material" target="#material">
<bind_vertex_input semantic="texcoord0" input_semantic="TEXCOORD" input_set="0"/>
</instance_material>
</technique_common>
</bind_material>
</instance_geometry>
</node>
</visual_scene>
</library_visual_scenes>
<library_geometries>
<geometry id="geometry">
<mesh>
<source id="geometry-positions">
<float_array id="geometry-positions-array" count="673731">*data*</float_array>
<technique_common>
<accessor count="224577" source="#geometry-positions-array" stride="3">
<param name="X" type="float"/>
<param name="Y" type="float"/>
<param name="Z" type="float"/>
</accessor>
</technique_common>
</source>
<source id="geometry-texcoord_0">
<float_array id="geometry-texcoord_0-array" count="449154">*data*</float_array>
<technique_common>
<accessor count="224577" source="#geometry-texcoord_0-array" stride="2">
<param name="S" type="float"/>
<param name="T" type="float"/>
</accessor>
</technique_common>
</source>
<vertices id="geometry-vertices">
<input semantic="POSITION" source="#geometry-positions"/>
</vertices>
<triangles count="329753" material="geometry_material">
<input offset="0" semantic="VERTEX" source="#geometry-vertices" set="0"/>
<input offset="1" semantic="TEXCOORD" source="#geometry-texcoord_0" set="0"/>
<p>*data*</p>
</triangles>
</mesh>
</geometry>
</library_geometries>
<library_materials>
<material id="material">
<instance_effect url="#material_effect"/>
</material>
</library_materials>
<library_effects>
<effect id="material_effect">
<profile_COMMON>
<newparam sid="material_effect-surface">
<surface type="2D">
<init_from>material_effect-image</init_from>
</surface>
</newparam>
<newparam sid="material_effect-sampler">
<sampler2D>
<source>material_effect-surface</source>
<wrap_s>CLAMP</wrap_s>
<wrap_t>CLAMP</wrap_t>
<minfilter>LINEAR_MIPMAP_LINEAR</minfilter>
<magfilter>LINEAR</magfilter>
<border_color>0 0 0 0</border_color>
</sampler2D>
</newparam>
<technique sid="t0">
<phong>
<emission>
<color>0 0 0 1</color>
</emission>
<ambient>
<color>1 1 1 1</color>
</ambient>
<diffuse>
<texture texture="material_effect-sampler" texcoord="texcoord0">
<extra type="color">
<technique profile="SCEI">
<color>1 1 1 1</color>
</technique>
</extra>
</texture>
</diffuse>
<specular>
<color>0 0 0 1</color>
</specular>
<shininess>
<float>0</float>
</shininess>
</phong>
</technique>
</profile_COMMON>
</effect>
</library_effects>
<scene>
<instance_visual_scene url="#defaultScene"/>
</scene>
<library-images>
<image id="material_effect-image" height="0" width="0">
<init_from>Tile_+037_+047_0.jpg</init_from>
</image>
</library-images>
</COLLADA>
lxml.etree.XMLSyntaxError: xmlSAX2Characters: huge text node, line 53, column 10001702
, которое вызывается строкойdoc = et.parse('my_file.xml')
. Похоже, у XSLT проблемы с длинными строками в моем файле. Вы знаете, возможно ли заставить XSLT обрабатывать даже эти строки?