Reputation: 6120
I have a json file that I need to update. I am converting it to a python dict (nested) to update it. Here is the input, but it could be any dept. I'm sure there is a better way to do this, but don't know.
Ultimatley I want to be able to perfom Create/Delete action in addition to the update.
# Now find TARGET value in nested key value chain
# Replace old value with NEWVALUE
import json
from pprint import pprint
d1 = open('jinputstack.json', 'r')
d1 = json.load(d1)
def traverse(obj, path=None, callback=None):
"""
Traverse Python object structure, calling a callback function for every element in the structure,
and inserting the return value of the callback as the new value.
"""
if path is None:
path = []
if isinstance(obj, dict):
value = {k: traverse(v, path + [k], callback)
for k, v in obj.items()}
elif isinstance(obj, list):
value = [traverse(elem, path + [[]], callback)
for elem in obj]
else:
value = obj
if callback is None:
# print("Starting value Found-----------------------------------------------------")
print(value)
return value
else:
print(path, value)
return callback(path, value)
def traverse_modify(obj, target_path, action):
"""
Traverses any arbitrary object structure and performs the given action on the value,
replacing the node with the
action's return value.
"""
target_path = to_path(target_path)
pprint(value)
pprint(target_path)
def transformer(path, value):
if path == target_path:
print(action)
d2 = data["groups"][0]["properties"][1]["value"]["data"][2]["object"]["name"].update(action)
return d2
else:
return value
return traverse(obj, callback=transformer)
def to_path(path):
"""
Helper function, converting path strings into path lists.
>>> to_path('foo')
['foo']
>>> to_path('foo.bar')
['foo', 'bar']
>>> to_path('foo.bar[]')
['foo', 'bar', []]
"""
if isinstance(path, list):
return path # already in list format
def _iter_path(path):
#pprint(path.split)
for parts in path.split('[]'):
for part in parts.strip('.').split('.'):
yield part
yield []
return list(_iter_path(path))[:-1]
def updateit(newvalue):
data["groups"][0]["properties"][1]["value"]["data"][2]["object"]["name"] = newvalue
print(data["groups"][0]["properties"][1]["value"]["data"][2]["object"]["name"])
return data["groups"][0]["properties"][1]["value"]["data"][2]["object"]["name"]
traverse_modify(d1, d1["groups"][0]["properties"][1]["value"]["data"][1]["object"]["name"], updateit("XXXXXXXXXXXXXX"))
json_data = json.dumps(data)
f = open("jinputstack.json","w")
f.write(json_data)
f.close()
jinputstack.json = {
"groups": [
{
"name": "group1",
"properties": [
{
"name": "Test-Key-String",
"value": {
"type": "String",
"encoding": "utf-8",
"data": "value1"
}
},
{
"name": "Test-Key-ValueArray",
"value": {
"type": "ValueArray",
"data": [
{
"data": true
},
{
"type": "Blob",
"object": {
"name": "John Su",
"age": 25,
"salary": 104000.45,
"married": false,
"gender": "Male"
}
}
]
}
}
],
"groups": [
{
"name": "group-child",
"properties": [
{
"name": "Test-Key-String"
},
{
"name": "Test-Key-List",
"value": {
"type": "List",
"data": [
"String1",
"String2",
"String3"
]
}
}
]
}
]
},
{
"name": "group2",
"properties": [
{
"name": "Test-Key2-String",
"value": {
"type": "String",
"encoding": "utf-8",
"data": "value2"
}
},
{
"name": "MicroBox"
}
]
}
]
}
Credit goes to original author: Vincent Driessen
Upvotes: 1
Views: 1486
Reputation: 2562
I think the best way would be to convert the Json object to XML and use ElementTree and XPath to parse and modify your object. Later you can revert to Json if you need:
import json
from xmljson import parker
from lxml.etree import Element
dataxml = parker.etree(datajson, root=Element('root'))
print(dataxml.find('.//data//name').text) # John Su
dataxml.find('.//data//name').text = "Joan d'Arc"
print(dataxml.find('.//data//name').text) # Joan d'Arc
print(json.dumps(parker.data(dataxml)))
There are some packages that do something like XPath on a Json string directly. One of them, jsonpath-rw
changes the syntax. I prefer to stick with the standard XPath syntax.
from jsonpath_rw import jsonpath, parse
expr = parse('$..data..name') # Notice that . is now $ and / is now .
# Confusing enough?
expr.find(datajson)[0] = 'yyyy'
print(expr.find(datajson)[0].value) # John Su
Another one xjpath
very simple and perhaps easier to learn, does not give you much difference than what you are doing now.
import xjpath
xj = xjpath.XJPath(datajson)
print(xj['[email protected][email protected][email protected]'])
# Not much different than your code:
print(data["groups"][0]["properties"][1]["value"]["data"][1]["object"]["name"])
I hope this helps.
Upvotes: 1