Alternatives to XML?

Peter Otten __peter__ at web.de
Thu Aug 25 09:58:24 EDT 2016


Frank Millman wrote:

> "Frank Millman"  wrote in message news:nplvvl$ci2$1 at blaine.gmane.org...
> 
>> Hi all
> 
>> I have mentioned in the past that I use XML for storing certain
>> structures 'off-line', and I got a number of comments urging me to use
>> JSON or YAML instead.
> 
>> Can anyone offer an alternative which is closer to my original intention?
> 
> Thanks to Chris and Peter for their additional input - much appreciated.
> 
> At the risk of disappointing some of you, this is how I am going to
> proceed.

'Tis too late for me to stop ;)

> 1. I am writing tools to make it easier to develop business systems, and
> at the same time I am developing a business system. As the tools mature I
> am spending more time on the system, but as I work on the system I am
> finding shortcomings in the tools, so I am bouncing between the two at the
> moment.
> 
> 2. There are many areas of the tools which other users will find confusing
> at first and will require explanations and documentation. I am more than
> ready to make changes based on the reactions I get. The subject of this
> thread is one small part of this.
> 
> 3. My immediate priority is to finish the business system, get it out
> there, and get feedback. Hopefully other users will then start dabbling
> with the tools and provide feedback there as well.
> 
> 4. As I have said already, for good or ill, I am comfortable with my
> current use of XML, so I do not have a pressing need to change to anything
> else. The problem that prompted this thread was the issue of storing '<'
> and '>' in attributes. I have come up with a simple workaround - pass the
> XML through a
> function between the database and the gui, converting from '>'  to '>'
> in one direction, and back to '>' in the other. It works.

As you have to keep the "<", why bother?
 
> 5. I have learned a lot from this thread, but for now it is staying in the
> back of my mind. If I ever get my project to the point where I need to
> move it to the front, I will know that I am getting somewhere!

At that point you may also look at my messy/buggy/incomplete attempt to 
convert between xml and python:

$ cat convert.py     
import ast
from xml.etree import ElementTree as etree

XML = """\
<case>
  <compare src="_param.auto_party_id" op="is_not" tgt="$None">
    <case>
      <on_insert>
        <auto_gen args="_param.auto_party_id"/>
      </on_insert>
      <not_exists>
        <literal value="<new>"/>
      </not_exists>
    </case>
  </compare>
</case>
"""

tree = etree.fromstring(XML)

TARGETS = {
    "$None": "None",
}
RTARGETS = {
    None: "$None",
}
assert len(RTARGETS) == len(TARGETS)

OPS = {
    "is_not": "is not",
}
ROPS = {ast.IsNot: "is_not"}
assert len(ROPS) == len(OPS)

NAMES = {"on_insert", "not_exists"}
FUNCS = {"auto_gen"}


def getchildren(elem):
    yield from elem.getchildren()


def to_python(elem, indent=""):
    # XXX build an AST rather than source code.
    if elem.tag == "compare":
        yield "{}if {} {} {}:".format(
            indent,
            elem.attrib["src"],
            OPS[elem.attrib["op"]],
            TARGETS[elem.attrib["tgt"]]
        )
        [child] = getchildren(elem)
        yield from to_python(child, indent)
    elif elem.tag in NAMES:
        yield "{}if {}:".format(indent, elem.tag)
        for child in getchildren(elem):
            yield from to_python(child, indent + "    ")
    elif elem.tag == "case":
        for child in getchildren(elem):
            yield from to_python(child, indent + "    ")
    elif elem.tag == "literal":
        yield "{}value = {!r}".format(indent, elem.attrib["value"])
    elif elem.tag in FUNCS:
        yield "{}auto_gen({})".format(indent, elem.attrib["args"])
    else:
        raise ValueError("Unknown tag {!r}".format(elem.tag))


def dotted(node):
    if isinstance(node, ast.Attribute):
        return dotted(node.value) + "." + node.attr
    else:
        return node.id


def to_xml(python):
        module = ast.parse(python)
        [body] = module.body
        root = etree.Element("case")

        def _convert(node, parent):
            if isinstance(node, ast.If):
                test = node.test
                if isinstance(test, ast.Compare):
                    compare = etree.Element("compare")
                    [op] = test.ops
                    compare.attrib["src"] = dotted(test.left)
                    compare.attrib["op"] = ROPS[type(op)]
                    right = test.comparators[0].value
                    compare.attrib["tgt"] = RTARGETS[right]
                    case = etree.Element("case")
                    compare.append(case)
                    parent.append(compare)
                    for child in node.body:
                        _convert(child, case)
                elif isinstance(test, ast.Name):
                    ename = etree.Element(test.id)
                    parent.append(ename)
                    for child in node.body:
                        _convert(child, ename)
            elif isinstance(node, ast.Expr):
                evalue = node.value
                evalue.func.id
                invoke = etree.Element(evalue.func.id)
                invoke.attrib["args"] = dotted(evalue.args[0])
                parent.append(invoke)
            elif isinstance(node, ast.Assign):
                assign = etree.Element("literal")
                [target] = node.targets
                assign.attrib["value"] = node.value.s
                parent.append(assign)
            else:
                global x
                x = node
                exit("unhandled")
        _convert(body, root)
        return root


# http://stackoverflow.com/questions/17402323/
# use-xml-etree-elementtree-to-write-out-nicely-formatted-xml-files
ElementTree = etree
from xml.dom import minidom


def prettify(elem):
    """Return a pretty-printed XML string for the Element.
    """
    rough_string = ElementTree.tostring(elem, 'utf-8')
    reparsed = minidom.parseString(rough_string)
    return reparsed.toprettyxml(indent="  ")

print("XML...")
print(XML)
print("\nbecomes Python...")
python = "\n".join(to_python(next(getchildren(tree))))
print(python)
print("\nbecomes XML:")
root = to_xml(python)
print(prettify(root))
$ python3 convert.py 
XML...
<case>
  <compare src="_param.auto_party_id" op="is_not" tgt="$None">
    <case>
      <on_insert>
        <auto_gen args="_param.auto_party_id"/>
      </on_insert>
      <not_exists>
        <literal value="<new>"/>
      </not_exists>
    </case>
  </compare>
</case>


becomes Python...
if _param.auto_party_id is not None:
    if on_insert:
        auto_gen(_param.auto_party_id)
    if not_exists:
        value = '<new>'

becomes XML:
<?xml version="1.0" ?>
<case>
  <compare op="is_not" src="_param.auto_party_id" tgt="$None">
    <case>
      <on_insert>
        <auto_gen args="_param.auto_party_id"/>
      </on_insert>
      <not_exists>
        <literal value="<new>"/>
      </not_exists>
    </case>
  </compare>
</case>






More information about the Python-list mailing list