Buscar etiquetas diferentes en dos archivos XML cuando el contenido es diferente

Buscar etiquetas diferentes en dos archivos XML cuando el contenido es diferente

De vez en cuando traduzco algunas aplicaciones de Android: el desarrollador me envía un archivo XML, por ejemplo

<string name="foo">Foo</string>
<string name="baz">Baz</string>

y le envío un archivo XML donde se ha traducido el contenido de cada elemento, por ejemplo

<string name="foo">Translated foo</string>
<string name="baz">Translated baz</string>

El problema surge cuando el desarrollador agrega algún elemento de texto nuevo y me envía un nuevo archivo para traducir, por ejemplo

<string name="foo">Foo</string>
<string name="bar">Bar</string>
<string name="baz">Baz</string>

Pregunta:¿Cómo puedo comparar esto con mi archivo traducido anterior buscando solo las etiquetas con nuevos atributos o, mejor aún, hay una manera simple (más o menos) de hacerlo?unirlos dos archivos, posiblemente agregando un marcador al comienzo de las nuevas líneas a traducir?

Dado el ejemplo anterior, esto significaría generar un archivo como

<string name="foo">Translated foo</string>
<!-- new --><string name="bar">Bar</string>
<string name="baz">Translated baz</string>

Respuesta1

Como no encontré ninguna solución preexistente, decidí escribir yo mismo un pequeño script de Python que hasta ahora parece funcionar:

"""Usage: merge_strings.py [-m <mrk>] [-o <file> [-i]] <old_xml> <new_xml>

Substitutes the content of any 'string' tag from <new_xml> with the
content of a 'string' tag from <old_xml> with the same 'name' attribute,
if present, otherwise prepends it with <mrk>.
By default the result is printed to stdout.

Note: This program assumes that no two 'string' tags in the same file
have the same 'name' attribute. Furthermore, 'string' tags with names
unique to <old_xml> are ignored.

Options:
    -h --help                 Show this screen.
    -m <mrk> --marker <mrk>   Marker for new strings [default: ***new***].
    -o <file>                 Print to <file> instead.
    -i --interactive          Check before overwriting <file>.
"""

from os.path import isfile
from sys import exit

from docopt import docopt
import lxml.etree as etree


def merge_strings(old, new, marker):
    """
    Merge in place synonymous strings from 'old' into 'new'.
    Ignores strings unique to 'old' and prepends strings unique to
    'new' with 'marker'.
    """
    for s in new.iterfind('//string'):
        name = s.attrib['name']
        t = old.find("//string[@name='" + name + "']")

        if t is not None:
            s.text = t.text
        else:
            s.text = marker + s.text

def check_overwrite(path):
    """
    Check if we want to overwrite 'path' and exit if not.
    Defaults to no.
    """
    print("About to overwrite:", path)
    choice = input("Continue? [y/N]")

    if choice.lower() != 'y':
        exit(0)

def print_to_file(tree, path, interactive=False):
    if interactive and isfile(path):
        check_overwrite(path)

    with open(path, mode='wb') as f:
        tree.write(f, pretty_print=True,
                      encoding='utf-8',
                      xml_declaration=True)

def print_to_stdout(tree):
    print(etree.tostring(tree, pretty_print=True,
                               encoding='utf-8',
                               xml_declaration=True).decode('utf-8'))


if __name__ == '__main__':
    args = docopt(__doc__)

    old_tree = etree.parse(args['<old_xml>'])
    new_tree = etree.parse(args['<new_xml>'])

    merge_strings(old_tree, new_tree, args['--marker'])

    if args['-o']:
        print_to_file(new_tree, args['-o'], args['--interactive'])
    else:
        print_to_stdout(new_tree)

Aquí está el resultado de ejemplo obligatorio:

$cat tests/old.xml 
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="foo">Translated foo</string>
  <string name="baz">Translated baz</string>
</resources>

$cat tests/new.xml 
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="foo">Foo</string>
  <string name="bar">Bar</string>
  <string name="baz">Baz</string>
</resources>

$python merge_strings.py old.xml new.xml                                                   
<?xml version='1.0' encoding='utf-8'?>
<resources>
  <string name="foo">Translated foo</string>
  <string name="bar">***new***Bar</string>
  <string name="baz">Translated baz</string>
</resources>

Observación:Soy relativamente nuevo en Python y completamente nuevo en XML, por lo que agradecería mucho cualquier comentario sobre cómo mejorar el código anterior.

información relacionada