Reputation: 27
I have an xml file in tmp/Program.ev3p
:
<?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>
I am trying to modify it with the following code:
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)
After running the code, my xml file contains:
<?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>
I need the namespaces to be in the tags they were registered in the original file instead of having all of them inside SourceFile
, is it possible to achieve so in python?
Upvotes: 1
Views: 606
Reputation: 107767
Currently, the undocumented ElementTree._namespace_map[uri] = prefix
is the older Python version (< 1.3) for namespace assignment to the more current, documented ElementTree.register_namespace(prefix, uri)
. But even this method does not resolve the root issue and docs emphasize this assignment applies globally and replaces any previous namespace or prefix:
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.
To achieve your desired result and because your XML is a bit complex with multiple default and non-default namespaces, consider 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 (e.g., Saxon, Xalan).
Specifically, you can use a temporary prefix like doc to map the default namespace of lowest level parent, VirtualInstrument and use this prefix to identify the needed nodes. All other elements are copied over as is with the identity transform template. Also, because you are adding an element to the default namespace you can assign it with the xsl:element
tag.
XSLT (save below as .xsl file, a special .xml file)
<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)
Upvotes: 1