如何在Python中解析JSON-XML混合文件

时间:2016-02-18 15:49:59

标签: python json regex parsing pyparsing

我想使用Python解析具有以下语法的文件(但嵌套次数不确定):

<XProtocol>
{
    <str1."fds"> "str2"
    <str3> 123.0
    <str4> { 1 2 3 4 5 6 6 "str" "str" 43 "str" 4543 }
    <weird1."str5">
    {
        <weird2."str6"> { "str" }
        <also."weird3"> 1
        <againweird> { 1 "fds" }
        { }
        <even> <more."weird4"> { } { } { } { "a" }
    }
}

所需的输出类似于:

'XProtocol':
{
    'str1."fds"': 'str2',
    'str3': 123.0,
    'str4': (1, 2, 3, 4, 5, 6, 6, 'str', 'str', 43, 'str', 4543),
    'weird1."str5"':
    {
        'weird2."str6"': ( 'str' ),
        'also."weird3"': 1,
        'againweird': ((1, 'fds'), None),
        'even': { 'more."weird4"': (None, None, None, 'a') },
    }
}  

我尝试使用以下代码失败:

import pyparsing as pp

def parse_x_prot(text):        
    lbra = pp.Literal('{').suppress()
    rbra = pp.Literal('}').suppress()
    lang = pp.Literal('<').suppress()
    rang = pp.Literal('>').suppress()
    dot = pp.Literal('.').suppress()
    cstr = pp.quotedString.addParseAction(pp.removeQuotes)
    tag = pp.Group(
        lang +
        pp.Word(pp.alphanums) +
        pp.Optional(pp.Group(dot + cstr)) +
        rang)
    val = pp.OneOrMore(
        cstr | pp.Word(pp.nums + '.')
    )
    exp = pp.Forward()
    exp << pp.OneOrMore(
        pp.Group(
            tag + pp.OneOrMore(
                (lbra + (val | exp) + rbra) |
                (val + exp)
            )
        )
    )
    return exp.parseString(text)

我一定是做错了什么,但还没有弄明白究竟是什么......更准确一点:以下代码告诉我它需要一个&#39;}&#39;}而不是一个新的标签&#39;。

3 个答案:

答案 0 :(得分:2)

有几件事:

tag的定义中,您将其包装在Group中,但我认为您确实想要使用Combine

第二件事,你在exp中的嵌套会将重复与递归混合起来。

这对我有用(同时,.suppress()上的dot):

tag = pp.Combine(
    lang +
    pp.Word(pp.alphas, pp.alphanums) +
    pp.Optional(dot + cstr) +
    rang).setName("tag")

exp = pp.Forward()
key_value = pp.Group(tag + exp)
number = pp.Regex(r'[+-]?\d+(\.\d*)?').setName("number")
exp <<= (number |
            cstr |
            key_value |
            pp.Group(lbra + pp.ZeroOrMore(exp) + rbra))

,并提供:

['XProtocol', [['str1.fds', 'str2'], ['str3', '123.0'], ...
[0]:
  XProtocol
[1]:
  [['str1.fds', 'str2'], ['str3', '123.0'], ['str4', ['1', '2', '3',...
  [0]:
    ['str1.fds', 'str2']
  [1]:
    ['str3', '123.0']
  [2]:
    ['str4', ['1', '2', '3', '4', '5', '6', '6', 'str', 'str', '43', ...
    [0]:
      str4
    [1]:
      ['1', '2', '3', '4', '5', '6', '6', 'str', 'str', '43', ...
  [3]:
    ['weird1.str5', [['weird2.str6', ['str']], ['also.weird3', ...
    [0]:
      weird1.str5
    [1]:
      [['weird2.str6', ['str']], ['also.weird3', '1'], ['againweird', ...
      [0]:
        ['weird2.str6', ['str']]
        [0]:
          weird2.str6
        [1]:
          ['str']
      [1]:
        ['also.weird3', '1']
      [2]:
        ['againweird', ['1', 'fds']]
        [0]:
          againweird
        [1]:
          ['1', 'fds']
      [3]:
        []
      [4]:
        ['even', ['more.weird4', []]]
        [0]:
          even
        [1]:
          ['more.weird4', []]
          [0]:
            more.weird4
          [1]:
            []
      [5]:
        []
      [6]:
        []
      [7]:
        ['a']

答案 1 :(得分:2)

我在代码中更改了一些内容以使其正常工作,我使用注释来指出出现了什么问题。

def parse_x_prot(text):
    # Set up some shortcuts
    lbra = pp.Literal('{').suppress()
    rbra = pp.Literal('}').suppress()
    lang = pp.Literal('<').suppress()
    rang = pp.Literal('>').suppress()
    dot = pp.Literal('.')
    cstr = pp.quotedString.addParseAction(pp.removeQuotes)

    # Define how a correct tag looks like (we use combine here to get the full tag in the output)
    tag = pp.Combine(
        lang +
        pp.Word(pp.alphanums) +
        pp.Optional(pp.Group(dot + pp.quotedString)) +
        rang)

    # Define legal value (first combine is for decimal values)
    val = pp.Combine(pp.Word(pp.nums) + dot + pp.Word(pp.nums)) | cstr | pp.Word(pp.nums)

    # Define the array with statement as recursion element
    statement = pp.Forward()
    array = pp.Group(pp.OneOrMore(tag) +
                     pp.OneOrMore(
                         (
                             # Note the one or more function here as we have 
                             # a kind of list here which will have elements
                             (lbra + pp.OneOrMore(val | statement) + rbra) |
                             val |
                             (lbra + rbra)
                         )
                     )
                     )

    statement << array
    return statement.parseString(text)

答案 2 :(得分:0)

这可能不是您想要的答案,但我认为Flex可以帮助您完成这类任务。甚至可能有一个python包装器