Adrian Coutsoftides
Adrian Coutsoftides

Reputation: 1293

How to write Python objects to a JSON file?

I have three classes in my program,

Processes contain Lanes.

Lanes contain Tasks.

The objects are stored in each other as arrays with some other string information about the class

I'm trying to output the Process objects into JSON files but I keep getting the following error:

Object of type 'Process' is not JSON serializable

I'm fairly new to JSON processing so I don't understand why the objects are outputting this error.

I'm using the following code to print the Process objects as JSON items:

def outputJSON(self):
        for n in self.processes:
            print(json.dumps(n, sort_keys=True, indent=4))

And these are the class objects:

class Task(object):

    def __init__(self, name, UiD):
        self.name = name
        self.UiD = UiD
        self.incoming = 'None'
        self.outgoing = []
        self.messageFlowIn = 'None'
        self.messageFlowOut = 'None'

    def __str__(self):
        print(self.name +"\n")
        print("Incoming Connection : \n" + self.incoming + "\n")
        print("Outgoing Connections : ")
        if len(self.outgoing) >= 1:
            for n in self.outgoing:
                print(n+"\n")
        print("MessageFlowIn : " + self.messageFlowIn)
        print("MessageFlowOut : " + self.messageFlowOut)


class Lane(object):

    def __init__(self, name, associatedTasks):
        self.name = name
        self.associatedTasks = associatedTasks

class Process(object):

    def __init__(self, name, associatedLaneNames):
        self.name = name
        self.associatedLaneNames = associatedLaneNames
        self.laneObjects = []

How can I correctly output this data to a JSON file?

Upvotes: 1

Views: 3010

Answers (3)

Adrian Coutsoftides
Adrian Coutsoftides

Reputation: 1293

Following @czr 's example; the solution was to go through and convert the object mappings into explicit dictionaries and then output an array of those nested dictionaries as follows:

def serialize(self):
    processArray =[]
    decisionItems = []

    for process in self.processes:
        processObj = {}
        laneArray = []
        for lane in process.laneObjects:
            laneObj = {}
            taskArray = []
            for task in lane.associatedTasks:
                taskObj = {}
                for variableName, value in task.__dict__.items():
                    temp = value
                    placeHolderArray =[]
                    if isinstance(value, Task) or isinstance(value, StartEvent) or isinstance(value, EndEvent):
                        temp = value.name
                        taskObj[variableName] = temp
                    elif isinstance(value,list): #fix these lines
                        for n in value:
                            if isinstance(n, Task) or isinstance(n, StartEvent) or isinstance(n, EndEvent) or isinstance(n, DecisionBranch):
                                placeHolderArray.append(n.name)
                        taskObj[variableName] = placeHolderArray
                    elif isinstance(value, DecisionBranch):
                        taskObj['junctionPath'] = task.junctionPath
                        decisionBranch = {}
                        decisionBranch['name'] = value.name
                        decisionBranch['options'] = value.decisionBranch
                        decisionBranch['source'] = value.source
                        #taskObj['DecisionTree'] = decisionBranch
                taskObj['URLs'] = task.urls
                taskArray.append(taskObj)

            laneObj['name'] = lane.name

            laneObj['associatedTasks'] = taskArray

            laneArray.append(laneObj)
        processObj['name'] = process.name
        processObj['laneObjects'] = laneArray
        processArray.append(processObj)
    return processArray


def outputJsonFile(self, fileContents):
    tempString = self.fileName.split('.')
    outputName = tempString[0]+'.json'
    with open(outputName, 'w') as outfile:
        outfile.write(json.dumps(fileContents, sort_keys=True, indent=4))

where fileContents takes the returned processArray from serialize()

The takeaway note is that iterating through a classes variables as follows:

for variableName, value in objectName.__dict__.items():

Allows you to retrieve the class variables and their respective values as a dictionary. As you is my code above, if it so happens that your value is an object then you have to explicitly define what properties you want to retrieve from that object by using python's handy isinstance():

if isinstance(value, Task) or isinstance(value, StartEvent) or isinstance(value, EndEvent):
                            temp = value.name
                            taskObj[variableName] = temp

If it is a python classic object, such as list; make sure to iterate through it explicitly:

elif isinstance(value,list): #fix these lines
                            for n in value:
                                if isinstance(n, Task) or isinstance(n, StartEvent) or isinstance(n, EndEvent) or isinstance(n, DecisionBranch):
                                    placeHolderArray.append(n.name)
                            taskObj[variableName] = placeHolderArray

The reason for my initial confusion was my prior experience with google's GSON for Java. That handy api simply takes a Java class and handily outputs a JSON formatted file for you. Unfortunately Python does not have such native libraries. The ideal solution would be to design your classes in ways such that they only contained native data types like strings,ints or lists of native datatypes. that way simply iterating over objectName.__dict__.item() will provide a neat enough solution for you.

Upvotes: 0

czr
czr

Reputation: 658

Assuming that your attributes are simple values or list, you can use the class .__dict__ attribute to convert the class to a dict and then serialize it to a json, as a example:

p = Process('name', 'lanename')
p.laneObjects.extend([Lane('name', [Task('name', 'uid')])])

def to_dict(obj):
    output ={}
    for key, item in obj.__dict__.items():
        if isinstance(item, list):
            l = []
            for item in item:
                d = to_dict(item)
                l.append(d)
            output[key] = l
        else:
            output[key] = item

    return output

to_dict(p)

This outputs:

{'associatedLaneNames': 'lanename',
 'laneObjects': [{'associatedTasks': [{'UiD': 'uid',
     'incoming': 'None',
     'messageFlowIn': 'None',
     'messageFlowOut': 'None',
     'name': 'name',
     'outgoing': []}],
   'name': 'name'}],
 'name': 'name'}

as a dict.

Upvotes: 2

Corey Bailey
Corey Bailey

Reputation: 55

Try using pickle to serialize and write to .JSON https://docs.python.org/3/library/pickle.html

Here's an example of how I save my data for a game I'm working on using pickle

def save():
    file_player = "Save/%s/player.JSON" % functions.player.name
    file_world = "Save/%s/world.JSON" % functions.player.name
    file_counter = "Save/%s/counter.JSON" % functions.player.name
    a = functions.player
    b = world.world_map
    c = functions.counter
    save_player = open(file_player, 'wb')
    save_world = open(file_world, 'wb')
    save_counter = open(file_counter, 'wb')
    pickle.dump(a, save_player, -1)
    pickle.dump(b, save_world, -1)
    pickle.dump(c, save_counter, -1)
    save_player.close()
    save_world.close()
    save_counter.close()

This is saving 3 different classes from 3 different files, and dumping them into 1 save folder.

Upvotes: -1

Related Questions