pythonxmlelementtreemusicxml

How to add a new Element to a musicXML tree in Python (elementtree)?


I am using Python to batch edit many musicXML files that currently look like this:

    <score-partwise>
    ...
      <attributes>
        <transpose>
          <diatonic>-5</diatonic>
          <chromatic>-9</chromatic>
          </transpose>
        </attributes>
    ...
      </score-partwise>

How can I add <octave-change>-1</octave-change> in <transpose></transpose>, as below?

    <score-partwise>
    ...
      <attributes>
        <transpose>
          <diatonic>-5</diatonic>
          <chromatic>-9</chromatic>
          <octave-change>-1</octave-change>
          </transpose>
        </attributes>
    ...
      </score-partwise>

I have attempted this:

import xml.etree.ElementTree as ET

attributes   = ET.Element("attributes")
attributes.append(ET.fromstring('<transpose><octave-change>-1</octave-change></transpose>'))

without success.

Any help is very much appreciated. Thank you.


Solution

  • Just find the element and append:

    x = """<score-partwise>    
          <attributes>
            <transpose>
              <diatonic>-5</diatonic>
              <chromatic>-9</chromatic>
              </transpose>
            </attributes>    
          </score-partwise>"""
    
    import xml.etree.ElementTree as et
    xml = et.fromstring(x)
    
    #
    xml.find("attributes").append(et.fromstring('<transpose><octave-change>-1</octave-change></transpose>'))
    
    print(et.tostring(xml))
    

    Which gives you:

    <score-partwise>
          <attributes>
            <transpose>
              <diatonic>-5</diatonic>
              <chromatic>-9</chromatic>
              </transpose>
            <transpose><octave-change>-1</octave-change></transpose></attributes>
    </score-partwise>
    

    That adds a new transpose element also, if you just want to append to the existing transpose element then select that.

    import xml.etree.ElementTree  as et
    
    xml = et.fromstring(x)
    
    
    xml.find(".//attributes/transpose").append(et.fromstring('<octave-change>-1</octave-change>'))
    
    print(et.tostring(xml))
    

    Which gives you:

    <score-partwise>
          <attributes>
            <transpose>
              <diatonic>-5</diatonic>
              <chromatic>-9</chromatic>
              <octave-change>-1</octave-change></transpose>
            </attributes>
    </score-partwise>
    

    You can also use SubElement which allows you to access the node:

    xml = et.fromstring(x)
    
    print(et.tostring(xml))
    e = et.SubElement(xml.find(".//attributes/transpose"), "octave-change")
    e.text = "-1"
    e.tail= "\n"
    

    If you want to formatting, you might find lxml to be a better option:

    import lxml.etree as et

    parser = et.XMLParser(remove_blank_text=True)
    xml = et.parse("test.xml",parser)
    
    
    xml.find(".//attributes/transpose").append(et.fromstring('<octave-change>-1</octave-change>'))
    xml.write('test.xml', pretty_print=True)
    

    Which will write:

    <score-partwise>
      <attributes>
        <transpose>
          <diatonic>-5</diatonic>
          <chromatic>-9</chromatic>
          <octave-change>-1</octave-change>
        </transpose>
      </attributes>
    </score-partwise>