将命名空间放入 Python 中的不同 XML 标签

Putting Namespaces into Different XML Tags in Python

我在 tmp/Program.ev3p 中有一个 xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<SourceFile Version="1.0.2.10" xmlns="http://www.ni.com/SourceModel.xsd">
    <Namespace Name="Project">
        <VirtualInstrument IsTopLevel="false" IsReentrant="false" Version="1.0.2.0" OverridingModelDefinitionType="X3VIDocument" xmlns="http://www.ni.com/VirtualInstrument.xsd">
            <FrontPanel>
                <fpruntime:FrontPanelCanvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:fpruntime="clr-namespace:NationalInstruments.LabVIEW.FrontPanelRuntime;assembly=NationalInstruments.LabVIEW.FrontPanelRuntime" xmlns:Model="clr-namespace:NationalInstruments.SourceModel.Designer;assembly=NationalInstruments.SourceModel" x:Name="FrontPanel" Model:DesignerSurfaceProperties.CanSnapToObjects="True" Model:DesignerSurfaceProperties.SnapToObjects="True" Model:DesignerSurfaceProperties.ShowSnaplines="True" Model:DesignerSurfaceProperties.ShowControlAdorners="True" Width="640" Height="480" />
            </FrontPanel>
            <BlockDiagram Name="__RootDiagram__">
                <StartBlock Id="n1" Bounds="0 0 70 91" Target="X3\.Lib:StartBlockTest">
                    <ConfigurableMethodTerminal>
                        <Terminal Id="Result" Direction="Output" DataType="Boolean" Hotspot="0.5 1" Bounds="0 0 0 0" />
                    </ConfigurableMethodTerminal>
                    <Terminal Id="SequenceOut" Direction="Output" DataType="NationalInstruments:SourceModel:DataTypes:X3SequenceWireDataType" Hotspot="1 0.5" Bounds="52 33 18 18" />
                </StartBlock>
            </BlockDiagram>
        </VirtualInstrument>
    </Namespace>
</SourceFile>

我正在尝试使用以下代码对其进行修改:

import xml.etree.ElementTree as ET

tree = ET.parse('tmp/Program.ev3p')
root = tree.getroot()

namespaces = {'http://www.ni.com/SourceModel.xsd': '' ,
              'http://www.ni.com/VirtualInstrument.xsd':'',
              'http://schemas.microsoft.com/winfx/2006/xaml/presentation':'',
              'http://schemas.microsoft.com/winfx/2006/xaml':'x',
              'clr-namespace:NationalInstruments.LabVIEW.FrontPanelRuntime;assembly=NationalInstruments.LabVIEW.FrontPanelRuntime':'fpruntime',
              'clr-namespace:NationalInstruments.SourceModel.Designer;assembly=NationalInstruments.SourceModel': 'Model',
              }

for uri, prefix in namespaces.items():
    ET._namespace_map[uri] = prefix

diagram = root[0][0][1]
elem = ET.Element('Data')
diagram.append(elem)

tree.write('tmp/Program.ev3p',"UTF-8",xml_declaration=True)

在 运行 代码之后,我的 xml 文件包含:

<?xml version='1.0' encoding='UTF-8'?>
<SourceFile xmlns="http://www.ni.com/SourceModel.xsd" xmlns="http://www.ni.com/VirtualInstrument.xsd" xmlns:Model="clr-namespace:NationalInstruments.SourceModel.Designer;assembly=NationalInstruments.SourceModel" xmlns:fpruntime="clr-namespace:NationalInstruments.LabVIEW.FrontPanelRuntime;assembly=NationalInstruments.LabVIEW.FrontPanelRuntime" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Version="1.0.2.10">
    <Namespace Name="Project">
        <VirtualInstrument IsReentrant="false" IsTopLevel="false" OverridingModelDefinitionType="X3VIDocument" Version="1.0.2.0">
            <FrontPanel>
                <fpruntime:FrontPanelCanvas Height="480" Width="640" Model:DesignerSurfaceProperties.CanSnapToObjects="True" Model:DesignerSurfaceProperties.ShowControlAdorners="True" Model:DesignerSurfaceProperties.ShowSnaplines="True" Model:DesignerSurfaceProperties.SnapToObjects="True" x:Name="FrontPanel" />
            </FrontPanel>
            <BlockDiagram Name="__RootDiagram__">
                <StartBlock Bounds="0 0 70 91" Id="n1" Target="X3\.Lib:StartBlockTest">
                    <ConfigurableMethodTerminal>
                        <Terminal Bounds="0 0 0 0" DataType="Boolean" Direction="Output" Hotspot="0.5 1" Id="Result" />
                    </ConfigurableMethodTerminal>
                    <Terminal Bounds="52 33 18 18" DataType="NationalInstruments:SourceModel:DataTypes:X3SequenceWireDataType" Direction="Output" Hotspot="1 0.5" Id="SequenceOut" />
                </StartBlock>
            <Data /></BlockDiagram>
        </VirtualInstrument>
    </Namespace>
</SourceFile>

我需要命名空间位于它们在原始文件中注册的标签中,而不是将它们全部放在 SourceFile 中,是否可以在 python 中实现?

目前,未记录的 ElementTree._namespace_map[uri] = prefix 是较旧的 Python 版本 (< 1.3),用于将命名空间分配给更新的、记录在案的 ElementTree.register_namespace(prefix, uri)。但即使是这种方法也不能解决根本问题,文档强调此分配适用于全局并替换任何以前的命名空间或前缀:

xml.etree.ElementTree.register_namespace(prefix, uri)
Registers a namespace prefix. The registry is global, and any existing mapping for either the given prefix or the namespace URI will be removed. prefix is a namespace prefix. uri is a namespace uri. Tags and attributes in this namespace will be serialized with the given prefix, if at all possible.


为了达到您想要的结果,并且由于您的 XML 有点复杂,具有多个默认和 non-default 名称空间,请考虑 XSLT, the special-purpose language to transform XML files. Python can run XSLT 1.0 scripts with the third-party module, lxml (not built-in etree). Additionally, XSLT is portable so very code can run in other languages (Java, C#, PHP, VB) and dedicated processors(例如 Saxon、Xalan)。

具体来说,您可以使用 doc 之类的临时前缀来映射最低级别父级 VirtualInstrument 的默认命名空间,并使用此前缀以确定所需的节点。使用 identity transform 模板复制所有其他元素。此外,因为您要将元素添加到默认命名空间,所以您可以使用 xsl:element 标记对其进行分配。

XSLT (下面另存为.xsl文件,一个特殊的.xml文件)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                              xmlns:doc="http://www.ni.com/VirtualInstrument.xsd">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <!-- IDENTITY TRANSFORM -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="doc:BlockDiagram">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
      <xsl:element name="Data" namespace="http://www.ni.com/VirtualInstrument.xsd"/>      
    </xsl:copy>
  </xsl:template>    

</xsl:stylesheet>

Python

import lxml.etree as ET

# LOAD XML AND XSL 
dom = ET.parse('Input.xml')
xslt = ET.parse('XSLTScript.xsl')

# TRANSFORM INPUT
transform = ET.XSLT(xslt)
newdom = transform(dom)

# OUTPUT RESULT TREE TO CONSOLE
print(newdom) 

# SAVE RESULT TREE AS XML
with open('Output.xml','wb') as f:
     f.write(newdom)

XSLT Demo