Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't get pyparsing Dict() to return nested dictionary

I'm trying to parse strings of the form:

'foo(bar:baz;x:y)'

I'd like the results to be returned in form of a nested dictionary, i.e. for the above string, the results should look like this:

{ 'foo' : { 'bar' : 'baz', 'x' : 'y' } }

Despite numerous combinations of Dict() and Group() I can't get it to work. My (one of the versions of) grammar looks like this:

import pyparsing as pp
field_name = pp.Word( pp.alphanums )
field_value = pp.Word( pp.alphanums )
colon = pp.Suppress( pp.Literal( ':' ) )

expr = pp.Dict( 
    pp.Group( 
        field_name + \
        pp.nestedExpr( 
            content = pp.delimitedList( 
                 pp.Group( field_name + colon + field_value ), 
                 delim = ';' 
            ) 
        ) 
    ) 
)

and now, the results are as follows:

In [62]: str = 'foo(bar:baz;x:y)'

In [63]: expr.parseString( str ).asList()
Out[63]: [['foo', [['bar', 'baz'], ['x', 'y']]]]

In [64]: expr.parseString( str ).asDict()
Out[64]: {'foo': ([(['bar', 'baz'], {}), (['x', 'y'], {})], {})}

In [65]: print( expr.parseString( str ).dump() )
Out[65]: [['foo', [['bar', 'baz'], ['x', 'y']]]]
         - foo: [['bar', 'baz'], ['x', 'y']]

So the asList() version looks quite good to me and should yield a dictionary I'm after I think. Of course given that (the way I understand it, please correct me) Dict() will parse lists of tokens by using the first element of the list as a key and all the rest as values of that key in a dictionary. This works insofar the dictionary is not nested. For example in such case:

expr = pp.Dict( 
    pp.delimitedList( 
        pp.Group( field_name + colon + field_value ), 
        delim = ';' 
    ) 
)

In [76]: expr.parseString( 'foo:bar;baz:x' ).asDict()
Out[76]: {'baz': 'x', 'foo': 'bar'}

So, the question is what is wrong with the first case (and my understanding of the problem) or perhaps Dict() can't cope with such case? I could use asList() and convert that manually into a dictionary, but I'd rather have pyparsing do it :)

Any help or directions would be greately appreciated.

Thank you.

like image 269
kgr Avatar asked Apr 03 '12 14:04

kgr


1 Answers

Two problems:

  • You are missing a pp.Dict around pp.delimitedList to make asDict on the inner result work correctly
  • You are only calling asDict on the outermost ParsingResult instance, leaving the inner ParsingResult "uninterpreted"

I tried the following:

from pyparsing import *
field_name = field_val = Word(alphanums)
colon = Suppress(Literal(':'))

expr = Dict(Group(
    field_name +
    nestedExpr(content =
        Dict(delimitedList( 
            Group(field_name + colon + field_value), 
            delim = ';' 
        ))
    )
))

Then used it like this:

>>> res = expr.parseString('foo(bar:baz;x:y)')
>>> type(res['foo'])
<class 'pyparsing.ParseResults'>
>>> { k:v.asDict() for k,v in res.asDict().items() }
{'foo': {'x': 'y', 'bar': 'baz'}}
like image 99
Niklas B. Avatar answered Oct 10 '22 18:10

Niklas B.