Procurando tags diferentes em dois arquivos XML quando o conteúdo é diferente

Procurando tags diferentes em dois arquivos XML quando o conteúdo é diferente

De vez em quando eu traduzo alguns aplicativos Android: o desenvolvedor me envia um arquivo XML, por exemplo

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

e eu lhe envio de volta um arquivo XML onde o conteúdo de cada elemento foi traduzido, por exemplo

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

O problema surge quando o desenvolvedor adiciona algum novo elemento de texto e me envia um novo arquivo para ser traduzido, por exemplo

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

Pergunta:Como posso comparar isso com meu arquivo traduzido anterior procurando apenas as tags com novos atributos ou, melhor ainda, existe uma maneira simples (ish) demesclaros dois arquivos, possivelmente adicionando um marcador no início das novas linhas a serem traduzidas?

Dado o exemplo anterior, isso significaria gerar um arquivo como

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

Responder1

Como não encontrei nenhuma solução pré-existente, decidi escrever eu mesmo um pequeno script Python que até agora parece dar conta do recado:

"""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)

Aqui está o exemplo de saída obrigatório:

$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>

Observação:Sou relativamente novo em Python e completamente novo em XML, portanto, qualquer comentário sobre como melhorar o código acima seria muito apreciado.

informação relacionada