JSON dump for User defined class in python - python

Here is how I want my data to be : (key=name, value=[dob,[misc1, misc2,..]])
# my sample code
inputNames = [
('james', ['1990-01-19', ['james1', 'james2', 'james3'] ]),
('julie', ['1991-08-07', ['julie1', 'julie2'] ]),
('mikey', ['1989-01-23', ['mikey1'] ]),
('sarah', ['1988-02-05', ['sarah1', 'sarah2', 'sarah3', 'sarah4'] ])
]
class empData (list):
def __init__ (self, misc=None):
list.__init__([])
# print('add empdata: ',misc[0],misc[1])
self.dob = misc[0]
self.extend(misc[1])
def edprint(self):
return(self.dob, self)
class myEmp():
def __init__ (self, anm, amisc=None):
self.nm = anm
self.details = empData(amisc)
def printme(self):
print(self.nm, self.details.edprint())
emps={}
for i in inputNames:
m = myEmp(i[0],i[1])
emps[m] = m
print(emps)
# prints addresses of variables
# for actual data use the following lines
for ea in emps:
emps[ea].printme()
try:
with open('data.json','w') as wfd:
json.dump(emps, wfd)
except IOError as ioerr:
print('File error: ',str(ioerr))
wfd.close()
The above gives me an error: TypeError: key <main.myEmp object at 0x10143d588> is not a string
I am unable to figure out how to dump my dict of myEmp data structures as JSON

Before you can dump to json you need explicitly convert your data to a serializable type like dict or list. You could do this using a list comprehension:
>>> d = [{'key':ea.nm, 'value':[ea.details.dob, ea.details]} for ea in emps]
>>> json.dumps(d)
'[{"value": ["1991-08-07", ["julie1", "julie2"]], "key": "julie"}, {"value": ["1989-01-23", ["mikey1"]], "key": "mikey"}, {"value": ["1990-01-19", ["james1", "james2", "james3"]], "key": "james"}, {"value": ["1988-02-05", ["sarah1", "sarah2", "sarah3", "sarah4"]], "key": "sarah"}]'

Related

How to replace value for a specific key in json file using python language

Json file
{
"payloadFormatVersion": "9.0",
"payload": {
"ServiceConfiguration": {
"LoggingSettings": {
"NumberOfLogFilesToKeep": 7,
"LogFileSizeBytes": 0,
"LogFolderPath": "C:\\demo\\logs\\feature\\",
"EnvironmentType": "testingenv",
"DataRelayLogSink": {
"PeriodInSeconds": 60,
"TargetAddress": "http://localhost:portNumber/dumm1",
"TargetTokenAddress": "http://localhost:portnumber/token",
"PayloadType": "somedata",
"TokenCredentials": {
"ClientId": "testclientid",
"ClientSecret": "testclientsecret",
"GrantType": "testgranttype"
}
}
},
}
}
JSON Content
def repalcejsonForSpecificKey(keyPath,fileName):
filePath = "C:\\rajesh\\Configurations\\" + fileName + "\\" + fileName + ".json"
print(filePath)
Dict = {}
with open(filePath) as f:
superHeroSquad = json.load(f)
duplicatedict={}
duplicatedict=superHeroSquad
testDict=getDictonaryItems(keyPath[0],**superHeroSquad)
print(testDict)
def getDictonaryItems(searchKey, duplicatedict):
if searchKey in duplicatedict.keys():
testDict = duplicatedict[searchKey]
return testDict
keyPath = ["payload","ServiceConfiguration", "TokenSettings", "ClientId"]
fileName="vestas.sdh.dr.gateway"
repalcejsonForSpecificKey(keyPath,fileName)
Below is my plan
Method1 accepts 2 arguments JsonParsingKeyWhereToBereplaced, filename
Redirecting Json file to dictionary
call the method2 recursively where it accepts 2 arguments, one with searchKey and other is dictonary, this method will return all the key&values from specific Key passed on the method call
Recursively call this method until and unless you reach downstream key and update the value if found
Trying to update nested value from Json file using python language
Note: I was able to update the value in the Json file directly with below line
superHeroSquad ['payload']['ServiceConfiguration']['TokenSettings']['ClientId'] = "text"
But not like below
superHeroSquad[keyPath[0][keyPath[1]][keyPath[2]][keyPath[3]] = "text"
You could traverse your json as a map and replace the specific values like this:
import json
def replace_json_for_specific_key(file: str, key_pairs: dict[str, any]):
content = json.load(open(file))
for k, v in key_pairs.items():
keys = k.split(".")
element = content
for key in keys[:-1]:
element = element.setdefault(key, {})
element[keys[-1]] = v
tmp_file = open(file, "w")
json.dump(content, tmp_file)
tmp_file.flush()
if __name__ == '__main__':
replace_json_for_specific_key(
"input.json",
{
"payload.ServiceConfiguration.LoggingSettings.NumberOfLogFilesToKeep": 90,
"payload.ServiceConfiguration.LoggingSettings.DataRelayLogSink.TokenCredentials.ClientId": "anothervalue"
}
)
Notice it will allow you to replace several values at once. You'll need to pass the dot (.) separated path to the specific key.

Python: dynamic json with string interpolation

I created a class of functions that provision some cloud infrastructure.
response = self.ecs_client.register_task_definition(
containerDefinitions=[
{
"name": "redis-283C462837EF23AA",
"image": "redis:3.2.7",
"cpu": 1,
"memory": 512,
"essential": True,
},
...
This is a very long json, I show just the beginning.
Then I refactored the code to use a parameter instead of the hard coded hash, memory and cpu.
response = self.ecs_client.register_task_definition(
containerDefinitions=[
{
"name": f"redis-{git_hash}",
"image": "redis:3.2.7",
"cpu": {num_cpu},
"memory": {memory_size},
"essential": True,
},
...
I read the values of git_hash, num_cpu and memory_size from a config file prior to this code.
Now, I also want to read to entire json from a file.
The problem is that if I save {num_cpu} etc. in a file, the string interpolation won't work.
How can I extract the json from my logic and still use string interpolation or variables?
You can use Template from string.
{
"name": "redis-${git_hash}",
"image": "redis:3.2.7",
"cpu": ${num_cpu},
"memory": ${memory_size},
"essential": true
}
from string import Template
import json
if __name__ == '__main__':
data = dict(
num_cpu = 1,
memory_size = 1,
git_hash = 1
)
with open('test.json', 'r') as json_file:
content = ''.join(json_file.readlines())
template = Template(content)
configuration = json.loads(template.substitute(data))
print(configuration)
# {'name': 'redis-1', 'image': 'redis:3.2.7', 'cpu': 1, 'memory': 1, 'essential': True}
Opinion: I think the overall approach is wrong. There is a reason why this method is not as popular as others. You can separate your configuration into two files (1) a static list of options and (2) your compact changeable configuration, and compose them in your code.
EDIT: You can create an object which reads the configuration from a standard (static or changeable) JSON file FileConfig. And then compose them using another object, something line ComposedConfig.
This will allow you to extend the behaviour, and add, for example, a run-time configuration in the mix. This way the configuration from your JSON file no longer depends on the run-time params, and you can separate what is changeable from what is static in your system.
PS: The get method is just an example for explaining the composed behaviour; you can use other methods/designs.
import json
from abc import ABC, abstractmethod
class Configuration(ABC):
#abstractmethod
def get(self, key: str, default: str) -> str:
pass
class FileConfig(Configuration):
def __init__(self, file_path):
self.__content = {}
with open(file_path, 'r') as json_file:
self.__content = json.load(json_file)
def get(self, key: str, default: str) -> str:
return self.__content.get(key, default)
class RunTimeConfig(Configuration):
def __init__(self, option: str):
self.__content = {'option': option}
def get(self, key: str, default: str) -> str:
return self.__content.get(key, default)
class ComposedConfig:
def __init__(self, first: Configuration, second: Configuration):
self.__first = first
self.__second = second
def get(self, key: str, default: str) -> str:
return self.__first.get(key, self.__second.get(key, default))
if __name__ == '__main__':
static = FileConfig("static.json")
changeable = FileConfig("changeable.json")
runTime = RunTimeConfig(option="a")
config = ComposedConfig(static, changeable)
alternative = ComposedConfig(static, runTime)
print(config.get("image", "test")) # redis:3.2.7
print(alternative.get("option", "test")) # a

json serialization of a list of objects of a custom class

I have a song class, which holds the attributes to a song, and it is a custom class. I also have a list of songs in a list called track list. When I try to json.dump the list, I get an error that says :
TypeError: Object of type 'Song' is not JSON serializable
How would I go about converting this list of songs to json?
Here is the additional relevant code that returns the error:
class Song:
def __init__(self, sname, sartist, coverart, albname, albartist, spotid):
self.sname = sname
self.sartist = sartist
self.coverart = coverart
self.albname = albname
self.albartist = albartist
self.spotid = spotid
tracklist = createDict(tracks) ##creates the list of songs, works fine
jsontracks = json.dumps(tracklist)
pp.pprint(jsontracks)
Thanks
I've solved this by adding an encode() method to the class:
def encode(self):
return self.__dict__
and adding some arguments to json.dumps:
jsontracks = json.dumps(tracklist, default=lambda o: o.encode(), indent=4)
This will "crawl" down your class tree (if you have any child classes) and encode every object as a json list/object automatically. This should work with just about any class and is fast to type. You may also want to control which class parameters get encoded with something like:
def encode(self):
return {'name': self.name,
'code': self.code,
'amount': self.amount,
'minimum': self.minimum,
'maximum': self.maximum}
or a little bit faster to edit (if you're lazy like me):
def encode(self):
encoded_items = ['name', 'code', 'batch_size', 'cost',
'unit', 'ingredients', 'nutrients']
return {k: v for k, v in self.__dict__.items() if k in encoded_items}
full code:
import json
class Song:
def __init__(self, sname, sartist, coverart, albname, albartist, spotid):
self.sname = sname
self.sartist = sartist
self.coverart = coverart
self.albname = albname
self.albartist = albartist
self.spotid = spotid
def encode(self):
return self.__dict__
tracklist = [
Song('Imagine', 'John Lennon', None, None, None, None),
Song('Hey Jude', 'The Beatles', None, None, None, None),
Song('(I Can\'t Get No) Satisfaction', 'The Rolling Stones', None, None, None, None),
]
jsontracks = json.dumps(tracklist, default=lambda o: o.encode(), indent=4)
print(jsontracks)
output:
[
{
"sname": "Imagine",
"sartist": "John Lennon",
"coverart": null,
"albname": null,
"albartist": null,
"spotid": null
},
{
"sname": "Hey Jude",
"sartist": "The Beatles",
"coverart": null,
"albname": null,
"albartist": null,
"spotid": null
},
{
"sname": "(I Can't Get No) Satisfaction",
"sartist": "The Rolling Stones",
"coverart": null,
"albname": null,
"albartist": null,
"spotid": null
}
]

matching key against different pairs in python

Due to different names of an attribute I need to match a key of a key value pare against a regex.
The possible names are defined in a dict:
MyAttr = [
('ref_nr', 'Reference|Referenz|Referenz-Nr|Referenznummer'),
('color', 'Color|color|tinta|farbe|Farbe'),
]
The import attributes from an item in another dict:
ImportAttr = [
('Referenz', 'Ref-Val'),
('color', 'red'),
]
Now I would like to return the value of the import attributes, if it is a known attribute (defined in my first dict MyAttr) matching different spelling of the attribute in question.
for key, value in ImportAttr:
if key == "Referenz-Nr" : ref = value
if key == "Farbe" : color = value
The goal is to return the value of a possible attribute if it is a known one.
print(ref)
print(color)
Should return the value if "Referenz-Nr" and "Farbe" are known attributes.
Obviously this pseudo code does not work, I just can't get my head around a function implementing regex for a key search.
It was not clear for me but maybe you want it:
#!/usr/bin/python3
MyAttr = [
('ref_nr', 'Reference|Referenz|Referenz-Nr|Referenznummer'),
('color', 'Color|color|tinta|farbe|Farbe')
]
ImportAttr = [
('Referenz', 'Ref-Val'),
('color', 'red'),
]
ref, color = None, None
for key, value in ImportAttr:
if key in MyAttr[0][1].split('|'):
ref = value
if key in MyAttr[1][1].split('|'):
color = value
print("ref: ", ref)
print("color: ", color)
The split can split the string into a list of string by the separator ("|" character here) then you can check is the key in that list or not.
The following solution is a little bit tricky. If you don't want to hardcode the positions into your source you can use locals().
#!/usr/bin/python3
MyAttr = [
('ref', 'Reference|Referenz|Referenz-Nr|Referenznummer'),
('color', 'Color|color|tinta|farbe|Farbe')
]
ImportAttr = [
('Referenz', 'Ref-Val'),
('color', 'red'),
]
ref, color = None, None
for var, names in MyAttr:
for key, value in ImportAttr:
if key in names.split('|'):
locals()[var] = value
break
print("ref: ", ref)
print("color: ", color)
If you want, you can also use pandas to solve this problem for the large data sets in this way.
get_references_and_colors.py
import pandas as pd
import re
import json
def get_references_and_colors(lookups, attrs):
responses = []
refs = pd.Series(re.split(r"\|", lookups[0][0]))
colors = pd.Series(re.split(r"\|", lookups[1][0]))
d = {"ref": refs, "color": colors}
df = pd.DataFrame(d).fillna('') # To drop NaN entries, in case if refs
# & colors are not of same length
# ref color
# 0 Reference Color
# 1 Referenz color
# 2 Referenz-Nr tinta
# 3 Referenznummer farbe
# 4 Farbe
for key, value in attrs:
response = {}
response["for_attr"] = key
df2 = df.loc[df["ref"] == key]; # find in 'ref' column
if not df2.empty:
response["ref"] = value
else:
df3 = df.loc[df["color"] == key]; # find in 'color' column
if not df3.empty:
response["color"] = value
else:
response["color"] = None # Not Available
response["ref"] = None
responses.append(response)
return responses
if __name__ == "__main__":
LOOKUPS = [
('Reference|Referenz|Referenz-Nr|Referenznummer', 'a'),
('Color|color|tinta|farbe|Farbe', 'b'),
]
ATTR = [
('Referenz', 'Ref-Val'),
('color', 'red'),
('color2', 'orange'), # improper
('tinta', 'Tinta-col')
]
responses = get_references_and_colors(LOOKUPS, ATTR) # dictionary
pretty_response = json.dumps(responses, indent=4) # for pretty printing
print(pretty_response)
Output
[
{
"for_attr": "Referenz",
"ref": "Ref-Val"
},
{
"for_attr": "color",
"color": "red"
},
{
"for_attr": "color2",
"color": null,
"ref": null
},
{
"for_attr": "tinta",
"color": "Tinta-col"
}
]

Getting certain information from string

I'm new to python as was wondering how I could get the estimatedWait and routeName from this string.
{
"lastUpdated": "07:52",
"filterOut": [],
"arrivals": [
{
"routeId": "B16",
"routeName": "B16",
"destination": "Kidbrooke",
"estimatedWait": "due",
"scheduledTime": "06: 53",
"isRealTime": true,
"isCancelled": false
},
{
"routeId":"B13",
"routeName":"B13",
"destination":"New Eltham",
"estimatedWait":"29 min",
"scheduledTime":"07:38",
"isRealTime":true,
"isCancelled":false
}
],
"serviceDisruptions":{
"infoMessages":[],
"importantMessages":[],
"criticalMessages":[]
}
}
And then save this to another string which would be displayed on the lxterminal of the raspberry pi 2. I would like only the 'routeName' of B16 to be saved to the string. How do I do that?
You just have to deserialise the object and then use the index to access the data you want.
To find only the B16 entries you can filter the arrivals list.
import json
obj = json.loads(json_string)
# filter only the b16 objects
b16_objs = filter(lambda a: a['routeName'] == 'B16', obj['arrivals'])
if b16_objs:
# get the first item
b16 = b16_objs[0]
my_estimatedWait = b16['estimatedWait']
print(my_estimatedWait)
You can use string.find() to get the indices of those value identifiers
and extract them.
Example:
def get_vaules(string):
waitIndice = string.find('"estimatedWait":"')
routeIndice = string.find('"routeName":"')
estimatedWait = string[waitIndice:string.find('"', waitIndice)]
routeName = string[routeIndice:string.find('"', routeIndice)]
return estimatedWait, routeName
Or you could just deserialize the json object (highly recommended)
import json
def get_values(string):
jsonData = json.loads(string)
estimatedWait = jsonData['arrivals'][0]['estimatedWait']
routeName = jsonData['arrivals'][0]['routeName']
return estimatedWait, routeName
Parsing values from a JSON file using Python?

Categories

Resources