How to change values in a json file using JsonPath in python - python

I have the following Json file: car_models.json
{
"name":"John",
"age":30,
"cars":
[
{
"car_model": "Mustang",
"car_brand": "Ford"
},
{
"car_model": "cx-5",
"car_brand": "Mazda"
}
]
}
I have another json file data_change.json, which contains details about the jsonpath and their values:
{
"testcase_ID": "test_1A",
"description": "Some description",
"request_change_data": [
{
"element_path": "$.cars.[0].car_model",
"element_value": "focus"
}
]
}
I want to read the data_change.json content, use the element_path from here, parse through car_models.json and update its value to the value from data_change.json.
As in, I want to use the jsonPath - $cars[0].car_model, parse through car_models.json, and change the value of car_model from Mustang to focus. So my updated car_models.json should be the following:
{
"name":"John",
"age":30,
"cars":
[
{
"car_model": "focus",
"car_brand": "Ford"
},
{
"car_model": "cx-5",
"car_brand": "Mazda"
}
]
}
How can I do this in python?

guessing that the Expected answer needs to have "focus" and not "ford"
The following should give you this:
import json
import re
with open('cars_model.json') as f:
cars_model = json.load(f)
with open('data_change.json') as f:
data_change = json.load(f)
for elements in data_change['request_change_data']:
element_path = elements['element_path']
#Reg ex to get you the number (as a string) between the square brackets
position_match = re.match(r"^.*\[(.*)\].*$", element_path)
position = int(position_match.group(1))
print position
# Split on "period" to get the thing to match
thing_to_change = element_path.split(".")[1]
print thing_to_change
value = elements['element_value']
print value
cars_model['cars'][0][thing_to_change] = value
print cars_model

Related

string indices must be integers when using python with json

I want to print the ip addresses from jobs.json but I am getting the error 'string indices must be integers'
Here is my python code:
import json
f = open('jobs.json')
data = json.load(f)
f.close()
for item in data["Jobs"]:
print(item["ip"])
And here is the Jobs.json file:
{
"Jobs": {
"Carpenter": {
"ip": "123.1432.515",
"address": ""
},
"Electrician": {
"ip": "643.452.234",
"address": "mini-iad.com"
},
"Plumber": {
"ip": "151.101.193",
"Address": "15501 Birch St"
},
"Mechanic": {
"ip": "218.193.942",
"Address": "Yellow Brick Road"
}
}
data["Company"] is a dictionary, so you're iterating over the keys (which are strings). Use data["Company"].values():
import json
with open("company.json", "r") as f_in:
data = json.load(f_in)
for item in data["Company"].values():
print(item["ip"])
Prints:
142.250.115.139
151.101.193
data["Company"] returns a dictionary. When iterating over that, you will get string keys for item, since that's what you get by default when iterating over a dictionary. Then you try to do item["ip"], where item is "Google" for example, which causes your error.
You want to iterate the values of the dictionary instead:
for item in data["Company"].values():
print(item["ip"])

Assign variable value in JSON payload fomat

I am using python 3.6.3a. I would like to generate payload for each of the json records. I am using each variable to access the record. How to assign variable value (each in this case) in payload? I tried {each} and other methods but didn't work.
code snippet below.
json_records = [{"description":"<p>This is scenario1<\/p>","owner":"deb",
"priority":"high"},
{"description":"<p>This is scenario2<\/p>","owner":"deb",
"priority":"medium"}]
json_object = json.loads(json_records)
for each in json_object:
payload = """
{
"subject": "test",
"fieldValues": [
{each}
]
}
"""
There are two ways to approach this problem.
One way could be creating a dict() object and inserting keys as you wish, then json.dumps(object) to convert into string payload as in:
import json
json_records = [{"description":"This is scenario1</p>","owner":"deb","priority":"high"}
,{"description":"This is scenario2</p>","owner":"deb","priority":"medium"}]
for obj in json_records:
payload = dict()
payload['subject'] = 'test'
for key,value in obj.items():
payload['fieldName'] = {
key:value
}
print(json.dumps(payload))
#{"subject": "test", "fieldName": {"priority": "high"}}
#{"subject": "test", "fieldName": {"priority": "medium"}}
Second way is to create a textual payload from string as in, however if you need a valid JSON at the end, this would require a post-step of validation (something like try json.loads(payload) - So I'd just use the first method. I would use this method only if I have a specific requirements to generate the payload in a certain way.
import json
json_records = [{"description":"This is scenario1</p>","owner":"deb","priority":"high"}
,{"description":"This is scenario2</p>","owner":"deb","priority":"medium"}]
# json_object = json.loads(json_records) # json.loads works only on byte-like strings. your object is already in python in this case.
for obj in json_records:
payload = """
{
"subject": "test",
"fieldValues": [
%s
]
}
""" % (obj["priority"])
print(payload)
#{
# "subject": "test",
# "fieldValues": [
# high
# ]
# }
#
#
# {
# "subject": "test",
# "fieldValues": [
# medium
# ]
# }
You could make payload a Template string and use it to put the data in each JSON record into the format you want. Bracket {} characters have not special meaning in Templates, which is what makes using them easy.
Doing that will create a valid string representation of a dictionary containing everything. You can turn this into an actual Python dictionary data-structure using the ast.literal_eval() function, and then convert that into JSON string format — which I think is the final format you're after.
rom ast import literal_eval
import json
from string import Template
from textwrap import dedent
json_records = '''[{"description":"<p>This is scenario1<\/p>","owner":"deb",
"priority":"high"},
{"description":"<p>This is scenario2<\/p>","owner":"deb",
"priority":"medium"}]'''
json_object = json.loads(json_records)
payload = Template(dedent("""
{
"subject": "test",
"fieldValues": [
$each
]
}""")
)
for each in json_object:
obj = literal_eval(payload.substitute(dict(each=each)))
print(json.dumps(obj, indent=2))
Output:
{
"subject": "test",
"fieldValues": [
{
"description": "<p>This is scenario1</p>",
"owner": "deb",
"priority": "high"
}
]
}
{
"subject": "test",
"fieldValues": [
{
"description": "<p>This is scenario2</p>",
"owner": "deb",
"priority": "medium"
}
]
}

Update json data with context in Python using jsonpath-ng

following Update json nodes in Python using jsonpath, would like to know how one might update the JSON data given a certain context.
So, say we pick the exact same JSON example:
{
"SchemeId": 10,
"nominations": [
{
"nominationId": 1
}
]
}
But this time, would like to double the value of the original value, hence some lambda function is needed which takes into account the current node value.
No need for lambdas; for example, to double SchemeId, something like this should work:
data = json.loads("""the json string above""")
jsonpath_expr = parse('$.SchemeId')
jsonpath_expr.find(data)
val = jsonpath_expr.find(data)[0].value
jsonpath_expr.update(data, val*2)
print(json.dumps(data, indent=2))
Output:
{
"SchemeId": 20,
"nominations": [
{
"nominationId": 1
}
]
}
Here is example with lambda expression:
import json
from jsonpath_ng import parse
settings = '''{
"choices": {
"atm": {
"cs": "Strom",
"en": "Tree"
},
"bar": {
"cs": "Dům",
"en": "House"
},
"sea": {
"cs": "Moře",
"en": "Sea"
}
}
}'''
json_data = json.loads(settings)
pattern = parse('$.choices.*')
def magic(f: dict, to_lang='cs'):
return f[to_lang]
pattern.update(json_data,
lambda data_field, data, field: data.update({field: magic(data[field])}))
json_data
returns
{
'choices': {
'atm': 'Strom',
'bar': 'Dům',
'sea': 'Moře'
}
}

Parsing muilti dimensional Json array to Python

I'm in over my head, trying to parse JSON for my first time and dealing with a multi dimensional array.
{
"secret": "[Hidden]",
"minutes": 20,
"link": "http:\/\/www.1.com",
"bookmark_collection": {
"free_link": {
"name": "#free_link#",
"bookmarks": [
{
"name": "1",
"link": "http:\/\/www.1.com"
},
{
"name": "2",
"link": "http:\/\/2.dk"
},
{
"name": "3",
"link": "http:\/\/www.3.in"
}
]
},
"boarding_pass": {
"name": "Boarding Pass",
"bookmarks": [
{
"name": "1",
"link": "http:\/\/www.1.com\/"
},
{
"name": "2",
"link": "http:\/\/www.2.com\/"
},
{
"name": "3",
"link": "http:\/\/www.3.hk"
}
]
},
"sublinks": {
"name": "sublinks",
"link": [
"http:\/\/www.1.com",
"http:\/\/www.2.com",
"http:\/\/www.3.com"
]
}
}
}
This is divided into 3 parts, the static data on my first dimension (secret, minutes, link) Which i need to get as seperate strings.
Then I need a dictionary per "bookmark collection" which does not have fixed names, so I need the name of them and the links/names of each bookmark.
Then there is the seperate sublinks which is always the same, where I need all the links in a seperate dictionary.
I'm reading about parsing JSON but most of the stuff I find is a simple array put into 1 dictionary.
Does anyone have any good techniques to do this ?
After you parse the JSON, you will end up with a Python dict. So, suppose the above JSON is in a string named input_data:
import json
# This converts from JSON to a python dict
parsed_input = json.loads(input_data)
# Now, all of your static variables are referenceable as keys:
secret = parsed_input['secret']
minutes = parsed_input['minutes']
link = parsed_input['link']
# Plus, you can get your bookmark collection as:
bookmark_collection = parsed_input['bookmark_collection']
# Print a list of names of the bookmark collections...
print bookmark_collection.keys() # Note this contains sublinks, so remove it if needed
# Get the name of the Boarding Pass bookmark:
print bookmark_collection['boarding_pass']['name']
# Print out a list of all bookmark links as:
# Boarding Pass
# * 1: http://www.1.com/
# * 2: http://www.2.com/
# ...
for bookmark_definition in bookmark_collection.values():
# Skip sublinks...
if bookmark_definition['name'] == 'sublinks':
continue
print bookmark_definition['name']
for bookmark in bookmark_definition['bookmarks']:
print " * %(name)s: %(link)s" % bookmark
# Get the sublink definition:
sublinks = parsed_input['bookmark_collection']['sublinks']
# .. and print them
print sublinks['name']
for link in sublinks['link']:
print ' *', link
Hmm, doesn't json.loads do the trick?
For example, if your data is in a file,
import json
text = open('/tmp/mydata.json').read()
d = json.loads(text)
# first level fields
print d['minutes'] # or 'secret' or 'link'
# the names of each of bookmark_collections's items
print d['bookmark_collection'].keys()
# the sublinks section, as a dict
print d['bookmark_collection']['sublinks']
The output of this code (given your sample input above) is:
20
[u'sublinks', u'free_link', u'boarding_pass']
{u'link': [u'http://www.1.com', u'http://www.2.com', u'http://www.3.com'], u'name': u'sublinks'}
Which, I think, gets you what you need?

How to format unicode strings to utf-8 in Python?

I'm reading in a JSON string which is littered with u'string' style strings. Example:
[
{
"!\/award\/award_honor\/honored_for": {
"award": {
"id": "\/en\/spiel_des_jahres"
},
"year": {
"value": "1996"
}
},
"guid": "#9202a8c04000641f80000000003a0ee6",
"type": "\/games\/game",
"id": "\/en\/el_grande",
"name": "El Grande"
},
{
"!\/award\/award_honor\/honored_for": {
"award": {
"id": "\/en\/spiel_des_jahres"
},
"year": {
"value": "1995"
}
},
"guid": "#9202a8c04000641f80000000000495ec",
"type": "\/games\/game",
"id": "\/en\/settlers_of_catan",
"name": "Settlers of Catan"
}
]
If I assign name = result.name. Then when I log of pass that value to a Django template, it displays as u'Dominion'
How do I format it to display as Dominion?
++ UPDATE ++
I think the problem has to do with printing values from a list or dictionary. For example:
result = freebase.mqlread(query)
games = {}
count = 0
r = result[0]
name = r.name
games["name"] = name,
self.response.out.write(games["name"])
self.response.out.write(name)
This displays as:
(u'Dominion',) // saved response to dictionary, and then printed
Dominion // when calling the value directly from the response
I need to iterate through an array of JSON items and the values are being shown with the unicode. Why?
The comma at the end of games["name"] = name, makes it a 1-tuple. Remove it.
>>> # example
>>> s = u"Jägermütze"
>>> s.encode("utf-8")
'J\xc3\xa4germ\xc3\xbctze'
>>> print s.encode("utf-8") # on a utf-8 terminal
Jägermütze
Don't know much about Django, but not accepting snicode strings seems unpythonic to me.
You can use str(your string) to do this.

Categories