【问题标题】:Writing a Python tool to convert XML to Python?编写 Python 工具将 XML 转换为 Python?
【发布时间】:2012-05-05 21:48:42
【问题描述】:

我被要求研究将 XML 转换为 Python 的可能性,以便可以将 XML 从当前进程中逐步淘汰,并用新的 Python 脚本替换它们。

目前 XML 文件由 python 和 ASP 使用,看起来基本上是这样的;

<?xml version="1.0" encoding="UTF-8"?>
<script>
    <stage id="stage1" nextStage="stage2">
        <initialise>
            <variable id="year" value="2012" type="Integer"/>
            <executeMethod class="engineclass_date" method="getTodayAsString">
                <arguments>
                    <variable id="someVar"/>
                </arguments>
                <return>
                    <variable id="todaysDate"/>
                </return>
            </executeMethod>

感谢Pepr 在从 XML 生成 Python 代码的工作中,我没有得到 parser/compiler。它仍然需要大量工作来处理所有可能的元素,但它确实有效,希望其他人可以像我一样从中学习!

仍然需要找到一种方法为脚本的每个阶段编写一个单独的文件,以便缩进能够正常工作并正确处理错误。

【问题讨论】:

标签: python xml xml-parsing elementtree


【解决方案1】:

使用标准的xml.etree.Element 树将信息从 XML 提取到 Python 对象(或具有相同 API 的更增强的第三方 lxml)。

我建议阅读 Mark Pilrim 的 Dive Into Python 3,第 12 章。XML (http://getpython3.com/diveintopython3/xml.html)。

这里是如何编写解析器/编译器的核心。这个想法是递归遍历元素,收集必要的信息并在可能的情况下输出代码:

import xml.etree.ElementTree as ET

class Parser:

    def __init__(self):
        self.output_list = []  # collected output lines
        self.il = 0            # indentation level


    def __iter__(self):
        return iter(self.output_list)


    def out(self, s):
        '''Output the indented string to the output list.'''
        self.output_list.append('    ' * self.il + s)


    def indent(self, num=1):
        '''Increase the indentation level.'''
        self.il += num


    def dedent(self, num=1):
        '''Decrease the indentation level.'''
        self.il -= num


    def parse(self, elem):
        '''Call the parser of the elem.tag name.

        The tag name appended to "parse_" and then the name of that
        function is called.  If the function is not defined, then
        self.parse_undefined() is called.'''

        fn_name = 'parse_' + elem.tag
        try:
            fn = getattr(self, fn_name)
        except AttributeError:
            fn = self.parse_undefined
        return fn(elem)


    def loop(self, elem):
        '''Helper method to loop through the child elements.'''
        for e in elem:
            self.parse(e)


    def parseXMLfile(self, fname):
        '''Reads the XML file and starts parsing from the root element.'''
        tree = ET.parse(fname)
        script = tree.getroot()
        assert script.tag == 'script'
        self.parse(script)


    ###################### ELEMENT PARSERS #######################

    def parse_undefined(self, elem):
        '''Called for the element that has no parser defined.'''
        self.out('PARSING UNDEFINED for ' + elem.tag)


    def parse_script(self, elem):
        self.loop(elem)


    def parse_stage(self, elem):
        self.out('')
        self.out('Parsing the stage: ' + elem.attrib['id'])
        self.indent()
        self.loop(elem)
        self.dedent()


    def parse_initialise(self, elem):
        self.out('')
        self.out('#---------- ' + elem.tag + ' ----------')
        self.loop(elem)


    def parse_variable(self, elem):
        tt = str   # default type
        if elem.attrib['type'] == 'Integer': 
            tt = int
        # elif ... etc for other types

        # Conversion of the value to the type because of the later repr().
        value = tt(elem.attrib['value'])  

        id_ = elem.attrib['id']

        # Produce the line of the output.
        self.out('{0} = {1}'.format(id_, repr(value)))


    def parse_execute(self, elem):
        self.out('')
        self.out('#---------- ' + elem.tag + ' ----------')
        self.loop(elem)


    def parse_if(self, elem):
        assert elem[0].tag == 'condition'
        condition = self.parse(elem[0])
        self.out('if ' + condition + ':')
        self.indent()
        self.loop(elem[1:])
        self.dedent()


    def parse_condition(self, elem):
        assert len(elem) == 0
        return elem.text


    def parse_then(self, elem):
        self.loop(elem)


    def parse_else(self, elem):
        self.dedent()
        self.out('else:')
        self.indent()
        self.loop(elem)


    def parse_error(self, elem):
        assert len(elem) == 0
        errorID = elem.attrib.get('errorID', None)
        fieldID = elem.attrib.get('fieldID', None)
        self.out('error({0}, {1})'.format(errorID, fieldID))


    def parse_setNextStage(self, elem):
        assert len(elem) == 0
        self.out('setNextStage --> ' + elem.text)


if __name__ == '__main__':
    parser = Parser()
    parser.parseXMLfile('data.xml')
    for s in parser:
        print s

当与粘贴在此处 http://pastebin.com/vRRxfWiA 的数据一起使用时,脚本会产生以下输出:

Parsing the stage: stage1

    #---------- initialise ----------
    taxyear = 2012
    taxyearstart = '06/04/2012'
    taxyearend = '05/04/2013'
    previousemergencytaxcode = '747L'
    emergencytaxcode = '810L'
    nextemergencytaxcode = '810L'

    ...

    maxLimitAmount = 0
    PARSING UNDEFINED for executeMethod
    if $maxLimitReached$ == True:
        employeepayrecord = 'N'
        employeepayrecordstate = '2'
    else:
        employeepayrecordstate = '1'
    gender = ''
    genderstate = '1'
    title = ''
    forename = ''
    forename2 = ''
    surname = ''
    dob = ''
    dobinvalid = ''

    #---------- execute ----------
    if $dobstring$ != "":
        validDOBCheck = 'False'
        PARSING UNDEFINED for executeMethod
        if $validDOBCheck$ == False:
            error(224, dob)
        else:
            minimumDOBDate = ''
            PARSING UNDEFINED for executeMethod
            validDOBCheck = 'False'
            PARSING UNDEFINED for executeMethod
            if $validDOBCheck$ == False:
                error(3007161, dob)
        if $dobstring$ == "01/01/1901":
            error(231, dob)
    else:
        error(231, dob)

Parsing the stage: stage2

    #---------- initialise ----------
    address1 = ''

    ...

【讨论】:

  • 谢谢。您确实理解正确。这是一个很大的帮助。我还不知道为什么需要import,但这是一个接近 970k LoC 的应用程序,所以我只是使用我所获得的信息,因为这有点“深入” ' 为了我。我在 XML 文件中发布了前几个阶段的 sn-p; pastebin.com/vRRxfWiA
  • @marksweb:似乎最好编辑你的问题,编辑sn-p的stage4部分(剪掉相同类型的重复内容,改进缩进以获得更好的可读性) ,并展示它应该如何翻译。我不知道APSX。 initialise 到底是什么意思,等等。你知道等效的 Python 代码应该是什么样子吗?
  • 这个不用python写,直接创建python即可。因此,逐行检查 xml 文件行,并在一个新文件中创建与该 xml 行等效的 python。
  • @marksweb:实际上,Python 是一个很好的语言来编写程序的转换。问题是 apsx-script 代码必须转换为服务器的 Python 等效代码。它不能逐行完成,因为两种语言不同。您必须了解原始版本的功能(语义)以及如何编写具有相同功能的 Python 等价物。说,分配变量是一个简单的片段。执行方法没那么简单。
  • 好吧,我明白你现在的意思了。如果 ASP 端被忽略,我要做的就是创建 python 并忽略任何其他可能使用 XML 的东西。
【解决方案2】:

如果您对这些 XML 文件有适当的 XML Schema,则可以使用 GenerateDS 等工具生成基于它们的 python 类。

这将允许您加载内存中的所有文件,并将它们作为对象。然后你如何将这些数据存储在其他地方......好吧,你没有说你想做什么,但你可以用python做任何你通常可以做的事情。

【讨论】:

  • 是的,我早些时候看到了,它会让生活更轻松。但是没有。我得看看生成或构建一个。
  • 我不太擅长基于 XML Schema 的转换;但是,XML 文件包含必须转换为 Python 的代码。在我看来,任何 XML 模式都不可能。但我可能错了。
  • @pepr 你完全误解了。 XSD 不是一种转换语言,我不建议您手动执行任何转换。 GenerateDS 将 XSD 作为输入,并输出 python 代码。 XSD 描述了 XML 文件的结构,这使得它可以用来生成代码。然后,您使用该代码将您的 xml 加载到内存中的对象中。
  • @marksweb 如果您还没有 XSD,它可能不会为您节省太多精力。
  • @Marcin:我不认为 XML Schema 是关于转换语言的。我知道它们用于描述 XML 文档类的结构和规则。问题在于,了解结构可能有助于通过 Python 类生成丰富的数据结构等价物,但它不包含有关如何重组脚本代码并用等价物替换以生成 Python 语言算法的知识。
猜你喜欢
  • 2010-09-08
  • 2013-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-28
  • 2019-07-27
  • 1970-01-01
  • 2012-02-17
相关资源
最近更新 更多