【问题标题】:Python: Working with XML files non-intrusivelyPython:以非侵入方式处理 XML 文件
【发布时间】:2018-06-21 15:48:44
【问题描述】:

我有大量的 XML 文件,我想在其中进行以下更改:

  1. 创建一个新元素——我们称之为new_element——在根元素下

  2. 找到另一个嵌套更深的特定元素——我们称之为existing_element——并移动它,使其成为new_element的子元素

我想以尽可能少的干扰方式来做到这一点,这样旧文件和新文件之间的差异只显示属于新创建元素的行的更改(因此已添加到文件中)或曾经属于被移动的元素(因此已从文件中删除)。我想使用 Python 3 来做到这一点。

但是,当我尝试使用 xml.dom.minidom 读取其中一个 XML 文件并将刚刚读取的内容写入一个新文件并比较这两个文件时,每一行都被标记为已更改(可能是因为它们包含不同类型的换行符)。此外,当我查看这两个文件的内容时,我发现 XML 声明中的编码规范以及声明后的换行符都消失了,并且整个文档中的标签中的属性都被打乱了顺序标签。

使用xml.etree.ElementTree 时的情况非常相似,只是现在整个 XML 声明都消失了,所有标签名称前面都有“ns0:”(出于某种原因),一些属性名称后面是“@987654327 @"。

这些对 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 已移入其中。

【问题讨论】:

    标签: python xml xml-parsing elementtree minidom


    【解决方案1】:

    是的,有一种称为XSLT 的方法,这是一种专用语言,旨在将 XML 文件从一种结构转换为另一种或其他格式,包括 HTML、TXT/CSV 甚至 JSON。 Python 的第三方模块lxml 可以运行XSLT 1.0 脚本(不是内置的minidometree 模块)。然而,其他语言(Java、C#、PHP、VB)和软件(Saxon、Xalan、libxslt、.NET)甚至2.0 and 3.0 scripts也可以运行这样的脚本。而 Python 可以通过命令行调用连接到这些外部解决方案。

    具体来说,运行身份转换以保持原始格式不变,然后将所需的更改应用于特定节点。一个挑战是在创建新元素 library-images 时处理需要定义前缀 docnamespace 的默认命名空间:

    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>
    

    Python

    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 Demo

    <?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>
    

    【讨论】:

    • 请查看更新后的答案以获得所需的结果。另外,请删除以上过去的cmets。
    • 谢谢,现在您的代码适用于我的示例!但是,当我在尚未删除数据的文件上尝试它时,我收到错误消息 lxml.etree.XMLSyntaxError: xmlSAX2Characters: huge text node, line 53, column 10001702,由行 doc = et.parse('my_file.xml') 触发。似乎 XSLT 在我的文件中存在长行问题。你知道是否可以让 XSLT 处理这些行?
    • 这不是 XSLT 问题,而只是一般的 XML 解析错误,因为无法读取原始文档。通常这是由于 XML 格式不正确(即损坏的标签、未转义的特殊字符)。我从来没有遇到过那个巨大的文本节点问题。如果您可以发布一个很好的示例,因为我有兴趣看到这样的文件。
    • 如果文件太大(问题会变得很长),我不知道如何在此处发布示例,但请考虑将所有三个“*data*”替换为,比如说, 20 MB 的实际数据——我认为你在这里输入什么数据并不重要,只要 XML 解析器将其视为文本——并且你拥有我正在处理的文件类型。
    • 然而,我最终还是使用了minidom;事实证明,在将生成文件中的换行符转换为与源文件中的换行符匹配之后,差异并没有那么糟糕。这绝对不是最不打扰的方式,但它足以满足我的需求。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-05
    • 1970-01-01
    • 1970-01-01
    • 2012-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多