如何在 Python 中解析 JSON-XML 混合文件
Posted
技术标签:
【中文标题】如何在 Python 中解析 JSON-XML 混合文件【英文标题】:How to parse JSON-XML hybrid file in Python 【发布时间】:2016-05-30 21:25:38 【问题描述】:我想用 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)
我一定是做错了什么,但还没有弄清楚到底是什么……更准确地说:下面的代码告诉我它需要一个“”而不是一个新的“标签”。
【问题讨论】:
那是......不是......“类似 JSON”......至少......!? 好吧..它遵循类似的语义,除了'字典'名称用''括起来,列表不是逗号分隔等等。XML会更远比较 它具有来自一堆其他序列化格式的 元素,但它与 JSON 的距离与我所说的与 XML 的距离一样... 这是什么意思?<even> <more."weird"> "a"
在尝试解析之前了解您尝试解析的数据格式可能是有用的第一步...... :o)
【参考方案1】:
有几点:
在您对tag
的定义中,您将其包装在Group
中,但我认为您确实想使用Combine
。
第二件事,您在exp
中的嵌套将重复与递归混为一谈。
这对我有用(另外,在dot
上使用.suppress()
):
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']
【讨论】:
非常感谢您的提示,它们对我帮助很大,您的代码实际上适用于大多数真实场景。然而,它看起来仍然没有产生正确的结果(例如,输出末尾的空项目都应该与more.weird4
处于同一级别)。我会玩弄它,看看会发生什么。【参考方案2】:
我在您的代码中更改了一些内容以使其正常工作,我使用 cmets 指示出了什么问题。
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)
【讨论】:
感谢 cmets,他们非常有帮助.. 不幸的是,上面的代码似乎不适用于更复杂的示例.. 我会在我更接近理解原因后立即回过头来和/或我可以生成一个简单的测试用例 请告诉我,看到这种混合的更奇怪的形式会很有趣:)【参考方案3】:这可能不是您想要的答案,但我认为Flex 会极大地帮助您完成此类任务。甚至可能有一个 python 包装器
【讨论】:
我不会反对这样一个事实,即有很多文本解析 - 其中一些非常高性能,但在易用性和 Python 接口方面,我发现pyparsing
实际上是一个很好的选择。以上是关于如何在 Python 中解析 JSON-XML 混合文件的主要内容,如果未能解决你的问题,请参考以下文章
编写混音单元测试时出错:URL 不可解析:remix_accounts.sol