2

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.

  • It would be helpful to see what output is generated from running your code, what was expected, and maybe a small example so others can reproduce your problem. – theWanderer4865 Apr 12 '16 at 20:40
  • @theWanderer4865 Once I get the full code working, I will update the question with the full code. The point of the program is to change certain fields of metadata in hundreds of musicXML files at a time. So if 100 files in a directory are for the "flute", the user can use this program to change the relevant XML metadata for the desired target instrument, say, "oboe". Ultimately the program writes new files. The code above is just a portion of the full program. – Andrew Niess Apr 12 '16 at 20:53

1 Answers1

2

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>
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • Instead of using `xml.find` where `xml` is the variable you defined with text from the document itself, could I use `tree.find(".//attributes/transpose").append(et.fromstring('-1'))` where `tree = ET.parse(i)` and `i` is each file in a directory being accessed with a for loop? – Andrew Niess Apr 13 '16 at 15:55
  • Let me try myself and I will report back :) – Andrew Niess Apr 13 '16 at 16:01
  • `tree.find(".//attributes/transpose").append(et.fromstring('-1'))` indeed works according to how `tree` is defined in my first comment. Thanks for your help Padraic. – Andrew Niess Apr 13 '16 at 16:10