Skip to main content

Diffable JSON Strings

If you need a JSON object/string to be consistently ordered regardless of how it was built or ordered in memory, the following script can help force it to be organized/ordered alphabetically so that it can be used with source control tools or use diff tools to compare different outputs to one another.

# Functions designed to be called via objectScript() in UI expressions.

from java.util import TreeMap
from com.inductiveautomation.ignition.common.util import Comparators
from com.inductiveautomation.ignition.common.gson import GsonBuilder

ciAlnumCmp = Comparators.alphaNumeric(False)

def __ordering(subject, listKey='name'):
	''''
	Deep copy with conversion of maps to key-ordered maps and
	conversion of lists-of-dicts that contain a 'name' key into
	ordered lists.
	Keys other than "name" may be supplied, or None to disable
	re-ordering lists of dictionaries.
	'''
	if hasattr(subject, 'items'):
		subst = TreeMap(ciAlnumCmp)
		for k, v in subject.items():
			subst[k] = __ordering(v)
		return subst
	if hasattr(subject, '__iter__'):
		# Use a generator to exit quickly if any element of the
		# list-like object is *not* a dictionary-like object.
		if listKey and all(hasattr(inner, 'items') for inner in subject):
			reordered = TreeMap(ciAlnumCmp)
			reordered.update([(x.get(listKey, str(i)), __ordering(x)) for (i, x) in enumerate(subject)])
			return reordered.values()
		return [__ordering(x) for x in subject]
	return subject
	
def orderedJson(json, useGson = True):
	'''
    Re-orders a JSON string alphabetically so it can be use with diff tools or other source control tools

	Args:
		json (str): JSON Dictionary as a string to be re-ordered
		useGson (bool): Use Google's Gson library to pretty-print the JSON

	Returns:
		str: Re-ordered JSON string
		
     '''
	source = system.util.jsonDecode(json)
	ordered = __ordering(source)

	if useGson:
		gson = GsonBuilder().setPrettyPrinting().create()
		return gson.toJson(ordered)
	else:
		return system.util.jsonEncode(dict(_=ordered), 2)[6:-1]

Source slightly tweaked from: https://forum.inductiveautomation.com/t/json-diffing-discussion/96800/26