Using Document Tags to Store JSON

Hi I’m trying to utilize document tags to store some data, and having a hard time with it.

Heres what I’m doing:
Create an empty document tag.
in scripting, create a dictionary, that will be a list of dictionaries.
write that structure to the tag
then I want to append additional entries to the list, which I get an error.

#get the tag value
tagData = system.tag.readBlocking([myDocTag])[0].value
#create an instance of data i want to store
instance = {'stuff':'stuff info'}
#if the tag is empty, create the data structure
if tagData is None:
	data = {'toplevel' : [instance]}
#the structure should exist, so just add to it
else:
	tagData = dict(tagData)
	tagData['toplevel'].append(instance) #I get an error here  
#can't convert to com.inductiveautomation.ignition.common.document.DocumentElement

data = system.util.jsonEncode(tagData)
system.tag.writeBlocking([myDocTag], [data])

Is there something I’m missing?

Well I think your first mistake is assuming the value you get from reading a document tag is a python dictionary.

You can convert it to a dictionary with something like this:

qv = system.tag.read("DocTest")
dict = system.util.jsonDecode(str(qv.value))

The next issue is that dictionaries don’t have an append method…

1 Like

You can also use .toDict():

qv = system.tag.read("DocTest")
doctest_dict = qv.value.toDict()

Also, just a quick warning. If you store java.util.Date type values to these tags, they get converted to a string and you will need to loop through the dict to parse them back to a Date object if you want to use them as a Date object again (system.date.parse(val_to_parse, "E MMM dd HH:mm:ss z yyyy")).

1 Like

Hrm, I do convert the value i get from a tag using
(after the else:)
tagData = dict(tagData)

I’ll try to use json decode like your example though.

I’m not trying to append to dict, i’m trying to append to a list thats the value of a key in dictionary. like this:

test = {'top': []}
test['top'].append(1)
test['top'].append(2)
test['top'].append(3)
print test
>>> {'top': [1, 2, 3]}

well after getting the tag value, I didnt realize I needed to force it to a string first. Thanks that helped!

@WillMT10’s toDict() method is a little cleaner than forcing it to string and parsing it.

1 Like

so is
system.tag.readBlocking([myDocTag])[0].value.toDict()
different than
dict(system.tag.readBlocking([myDocTag])[0].value) ?

Yes, because toDict() is something we implemented on the Python-friendly document type you get from reading that tag’s value. I don’t know how dict() will behave when receiving that object.

oh ok that makes sense. sweet well i’ll try that, thanks!

Is this something recent, or did I completely miss it somewhere? Up til now I was importing and using the functions in TypeUtilities.

I didn’t know it was there either… but it looks like it has been since 2018.

1 Like

A word of caution based on my observations...

JSON at its top level is always a dictionary of key/value pairs but the Ignition document tag seems to accept objects with lists at the top level.

The object returned by reading the value of a document tag will have a PyDocumentObjectAdapter object for levels of the object that are dictionary (dict) equivalents.

The object returned by reading the value of a document tag will have a will have an array.array object for levels of the document that are list equivalents.

Calling .toDict() on a PyDocumentObjectAdapter object will return the object convert to a py dict object with deeper level PyDocumentObjectAdapter objects also converted to py dict objects and with deeper level array.array objects converted to py list objects.

Calling .tolist() on an array.array object will return will return the object convert to a py list object but DOES NOT convert deeper level PyDocumentObjectAdapter and array.array objects. You cannot assume some lower level isn't still a PyDocumentObjectAdapter object or array.array object. In that case you may need to recursively process the whole object

def sanitize(obj):
	if hasattr(obj, "append"): # list like object
		if hasattr(obj, "tolist"): # array.array object 
			obj = obj.tolist()
		for i in range(len(obj)):
			obj[i] = sanitize(obj[i])
		return obj
	elif hasattr(obj, "items"): # dict like object
		if hasattr(obj, "toDict"): # PyDocumentObjectAdapter object
			obj = obj.toDict()
			# We should not need to process the deeper levels
#		else:
#			for k, v in obj.iteritems():
#				obj[k] = sanitize(v)
		return obj
	else:
		return obj


obj_in =[{"a": [1,2], "b": 3, "c": {"x":4}}, {"y":5}, 100]
system.tag.writeBlocking([tagPath], [obj_in])
obj_out = system.tag.readBlocking([tagPath])[0].value
results = sanitize(obj_in)
print results
results.append(300)
results[0]["a"].append(3)
results[0]["c"]["y"] = 5
print results

Therefore it would seem good practice to always have the top level of the object be a dictionary so you can just call .toDict() on the whole thing.

4 Likes