Python: Работа с файлами XML не навязчиво

1

У меня есть большое количество XML файлов, в которых я хочу внести следующие изменения:

  1. Создайте новый элемент - позвольте ему new_element - под корневым элементом

  2. Найдите другой, специфический элемент, который более глубоко вложен - позвоните ему в 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 элемента было перемещено в Это.

Теги:
minidom
xml-parsing
elementtree

1 ответ

2

Да, существует способ, называемый 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)

Выход

Демо-версия XSLT Fiddle

<?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>
  • 0
    Пожалуйста, смотрите обновленный ответ, дающий желаемый результат. Также, пожалуйста, удалите предыдущие комментарии.
  • 0
    Спасибо, теперь ваш код работает на моем примере! Однако, когда я пробую его на файле, в котором данные не были удалены, я получаю сообщение об ошибке lxml.etree.XMLSyntaxError: xmlSAX2Characters: huge text node, line 53, column 10001702 , которое вызывается строкой doc = et.parse('my_file.xml') . Похоже, у XSLT проблемы с длинными строками в моем файле. Вы знаете, возможно ли заставить XSLT обрабатывать даже эти строки?
Показать ещё 4 комментария

Ещё вопросы

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