Rasmus Eskesen
Rasmus Eskesen

Reputation: 246

Add tag with content to existing XML (resx) using python

I have an XML with a number of strings:

<?xml version="1.0" encoding="UTF-8"?>
  <Strings>
    <String id="TEST_STRING_FROM_XML">
      <en>Test string from XML</en>
      <de>Testzeichenfolge aus XML</de>
      <es>Cadena de prueba de XML</es>
      <fr>Tester la chaîne à partir de XML</fr>
      <it>Stringa di test da XML</it>
      <ja>XMLからのテスト文字列</ja>
      <ko>XML에서 테스트 문자열</ko>
      <nl>Testreeks van XML</nl>
      <pl>Łańcuch testowy z XML</pl>
      <pt>Cadeia de teste de XML</pt>
      <ru>Тестовая строка из XML</ru>
      <sv>Teststräng från XML</sv>
      <zh-CHS>从XML测试字符串</zh-CHS>
      <zh-CHT>從XML測試字符串</zh-CHT>
      <Comment>A test string that comes from a shared XML file.</Comment>
    </String>
    <String id="TEST_STRING_FROM_XML_2">
      <en>Another test string from XML.</en>
      <de></de>
      <es></es>
      <fr></fr>
      <it></it>
      <ja></ja>
      <ko></ko>
      <nl></nl>
      <pl></pl>
      <pt></pt>
      <ru></ru>
      <sv></sv>
      <zh-CHS></zh-CHS>
      <zh-CHT></zh-CHT>
      <Comment>Another test string that comes from a shared XML file.</Comment>
    </String>
  </Strings>

And I would like to append these strings to a resx file with a long list of strings in the following format:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <!-- 
    Microsoft ResX Schema 
    
    Version 2.0
    
    **a bunch of schema and header stuff...**
    -->
    
  <data name="STRING_NAME_1" xml:space="preserve">
    <value>This is a value 1</value>
    <comment>This is a comment 1</comment>
  </data>
  <data name="STRING_NAME_2" xml:space="preserve">
    <value>This is a value 2</value>
    <comment>This is a comment 2</comment>
  </data>
</root>

But using the following snippet of python code:

import sys, os, os.path, re
import xml.etree.ElementTree as ET
from xml.dom import minidom

existingStrings = []
newStrings = {}
languages = []

resx = '*path to resx file*'


def LoadAllNewStrings():

    src_root = ET.parse('Strings.xml').getroot()

    for src_string in src_root.findall('String'):

        src_id = src_string.get('id')

        src_value = src_string.findtext("en")
        src_comment = src_string.findtext("Comment")

        content = [src_value, src_comment]

        newStrings[src_id] = content


def ExscludeExistingStrings():
    dest_root = ET.parse(resx)
    for stringName in dest_root.findall('Name'):
        for stringId in newStrings:
            if stringId == stringName:
                newStrings.remove(stringId)


def PrettifyXML(element):

    roughString = ET.tostring(element, 'utf-8')
    reparsed = minidom.parseString(roughString)
    return reparsed.toprettyxml(indent="  ")


def AddMissingStringsToLocalResource():

    ExscludeExistingStrings()
    
    with open(resx, "a") as output:
        root = ET.parse(resx).getroot()

        for newString in newStrings:
            
            data = ET.Element("data", name=newString)

            newStringContent = newStrings[newString]
            newStringValue = newStringContent[0]
            newStringComment = newStringContent[1]

            ET.SubElement(data, "value").text = newStringValue
            ET.SubElement(data, "comment").text = newStringComment

            output.write(PrettifyXML(data))


if __name__ == "__main__":
    LoadAllNewStrings()
    AddMissingStringsToLocalResource()

I get the following XML appended to the end of the resx file:

  <data name="STRING_NAME_2" xml:space="preserve">
    <value>This is a value 1</value>
    <comment>This is a comment 1</comment>
  </data>
</root><?xml version="1.0" ?>
<data name="TEST_STRING_FROM_XML">
  <value>Test string from XML</value>
  <comment>A test string that comes from a shared XML file.</comment>
</data>
<?xml version="1.0" ?>
<data name="TEST_STRING_FROM_XML_2">
  <value>Another test string from XML.</value>
  <comment>Another test string that comes from a shared XML file.</comment>
</data>

I.e. the root ends and then my new strings are added after. Any ideas on how to add the data tags to the existing root properly?

Upvotes: 0

Views: 755

Answers (1)

Tomalak
Tomalak

Reputation: 338228

with open(resx, "a") as output:

No. Don't open XML files as text files. Not for reading, not for writing, not for appending. Never.

The typical life cycle of an XML file is:

  • parsing (with an XML parser)
  • reading or Modification (with a DOM API)
  • if there were changes: Serializition (also with a DOM API)

At no point should you ever call open() on an XML file. XML files are not supposed to be treated as if they were plain text. They are not.

# parsing
resx = ET.parse(resx_path)
root = resx.getroot()

# modification
for newString in newStrings:
    newStringContent = newStrings[newString]

    # create node
    data = ET.Element("data", name=newString)
    ET.SubElement(data, "value").text = newStringContent[0]
    ET.SubElement(data, "comment").text = newStringContent[1]
    
    # append node, e.g. to the top level element
    root.append(data)
    
# serialization
resx.write(resx_path, encoding='utf8')

Upvotes: 1

Related Questions