#!/usr/bin/env python3 import sys from xml.etree import ElementTree class Text(ElementTree.Element): pass class CustomTreeBuilder(ElementTree.TreeBuilder): def start(self, tag, attrs): self.ctext = [''] super().start(tag, attrs) def end(self, tag): self.ctext = [''] super().end(tag) def comment(self, text): self.ctext = [''] super().comment(text) def data(self, data): if not self.ctext: self.ctext = [''] if self.ctext[0]: self.ctext[0] += data else: data = data.lstrip() self.ctext[0] += data if data: super().start(Text, {"text": self.ctext}) super().end(Text) def convert(inp=sys.stdin, outp=sys.stdout): tree = ElementTree.parse(inp, ElementTree.XMLParser(target=CustomTreeBuilder(insert_comments=True))) root = tree.getroot() def sub(e, n, nosp=False): if e.tag == Text: etag = '!' elif e.tag == ElementTree.Comment: etag = '!--' else: etag = e.tag.replace('\\','\\\\').replace('>','\\>').replace('/','\\/').replace(' ','\\ ') if etag[0] == '!': etag = '\\!' + etag text='' if etag == '!--': text = e.text elif etag == '!': text = e.attrib['text'][0] if text: text = text.strip() if not nosp: outp.write(' ' * n) outp.write(f'<{etag}') if e.tag != Text: for key, value in e.attrib.items(): key = key.replace('\\','\\\\').replace('>','\\>').replace('/','\\/').replace('=','\\=') outp.write(f' {key}') if value is not None: value = value.replace('\\','\\\\').replace('"','\\"') outp.write(f'="{value}"') if len(e) or text: outp.write('>') if text: if '\n' in text: outp.write('\n') outp.write(text.replace('\\','\\\\').replace('<','\\<')) if '\n' in text: outp.write('\n' + (' ' * (n - nosp * 2))) else: onlytext = len(e) == 1 and not isinstance(e[0].tag, str) if not onlytext: outp.write('\n') for child in e: sub(child, n+2, onlytext) if not onlytext: outp.write(' ' * n) outp.write(f'') else: outp.write('/>') if not nosp: outp.write('\n') sub(root, 0) if __name__ == '__main__': convert()