python - JSON将重复条目转换为数组,但恢复原始顺序
问题描述
我有一个带有重复条目的 JSON 字符串。我已经学会了如何在调用 json.loads(string) 时将它们转换为数组以保留所有这些,参见例如https://stackoverflow.com/a/61416136/7471760。
现在的问题是,在某些情况下,我有非常奇怪的输入 JSON 字符串(我无法更改),我确实需要在“不同”重复项集之间保留顺序。例如:
jstring = '\
{\
"anna": { "age": 23, "color": "green"},\
"john": { "age": 35, "color": "blue"},\
"laura":{ "age": 32, "color": "red"},\
"john": { "age": 31, "color": "black"},\
"anna": { "age": 41, "color": "pink"}\
}'
现在我使用这个钩子将此字符串转换为 JSON 对象,但不会丢失重复项(不同的学生)。
def array_on_duplicates(ordered_pairs):
d = {}
for k, v in ordered_pairs:
if k in d:
if type(d[k]) is list:
d[k].append(v)
else:
d[k] = [d[k],v]
else:
d[k] = v
return d
但是,我仍然需要恢复这些学生的原始列表顺序(第一个输入 - 第一个输出)。
使用 json.loads() 时,我有所有条目,但我失去了原来的顺序:
json.loads(jstring, object_pairs_hook=array_on_duplicates)
{'anna': [{'age': 23, 'color': 'green'}, {'age': 41, 'color': 'pink'}],
'john': [{'age': 35, 'color': 'blue'}, {'age': 31, 'color': 'black'}],
'laura': {'age': 32, 'color': 'red'}}
解决这个问题的最有效方法是什么?(除了更改繁琐的输入字符串,不幸的是我不能)。
解决方案
我想到的一种解决方法是按照此处的建议为每个条目 anna_1、anna_2 等生成重复的密钥:https ://stackoverflow.com/a/29323197/7471760 ,这样一个人就可以拥有唯一的条目,然后挂钩这对到 OrderedDict。
其他选择是直接在钩子中返回键值元组并稍后处理它https://stackoverflow.com/a/29322077/7471760。
但是,保留数组结构对我来说非常有用,最适合我的是使用这种解决方法,将顺序显式保留在一个额外的键中:
def array_on_duplicates_keep_order(ordered_pairs):
"""Convert duplicate keys to arrays and store order on an extra key."""
# https://www.semicolonworld.com/question/56998/python-json-parser-allow-duplicate-keys
# https://stackoverflow.com/questions/14902299/json-loads-allows-duplicate-keys-in-a-dictionary-overwriting-the-first-value
d = {}
order = 0
for k, v in ordered_pairs:
if type(v) is dict:
v['o'] = order
if k in d:
if type(d[k]) is list:
d[k].append(v)
else:
d[k] = [d[k],v]
else:
d[k] = v
order += 1
return d
产生:
jobj = json.loads(jstring, object_pairs_hook=array_on_duplicates_keep_order)
{'anna': [{'age': 23, 'color': 'green', 'o': 0},
{'age': 41, 'color': 'pink', 'o': 4}],
'john': [{'age': 35, 'color': 'blue', 'o': 1},
{'age': 31, 'color': 'black', 'o': 3}],
'laura': {'age': 32, 'color': 'red', 'o': 2}}
最后,我可以通过使用命名元组并按顺序键排序来恢复学生的原始顺序:
class Student(NamedTuple):
name: str
age: int
color: str
o: int
studentList = []
for k, v in jobj.items():
if not type(v) is list:
studentList.append(Student(k, v['age'], v['color'], v['o']))
else:
for s in v:
studentList.append(Student(k, s['age'], s['color'], s['o']))
orderedList = sorted(studentList, key=lambda s: s.o)
这给了我想要的东西,而无需更改输入并仍然使用 JSON 作为中间存储变量:
studentList
[Student(name='anna', age=23, color='green', o=0),
Student(name='anna', age=41, color='pink', o=4),
Student(name='john', age=35, color='blue', o=1),
Student(name='john', age=31, color='black', o=3),
Student(name='laura', age=32, color='red', o=2)]
orderedList
[Student(name='anna', age=23, color='green', o=0),
Student(name='john', age=35, color='blue', o=1),
Student(name='laura', age=32, color='red', o=2),
Student(name='john', age=31, color='black', o=3),
Student(name='anna', age=41, color='pink', o=4)]
推荐阅读
- python - Django - TabularInline 没有添加按钮
- python - Python - 循环不同的值并将其传递给变量
- c# - 开始交易出错
- python - 提出了字符串的序列化算法,但仅适用于长度小于 10 个字母的单词。
- javascript - 如何从 url javascript 获取数据并显示内容
- reactjs - 为什么 mapStateToProps 中的状态未定义?
- android - 使用 Sqlite 表为每个活动动态创建活动?
- javascript - 如何使用我的 chrome 扩展程序在特定页面上调用脚本?
- java - Spring-mvc 设置问题
- jestjs - 如何模拟指定的请求使用玩笑?