How to Deserialize Complex JSON in my Binding?

In the binding that I am working on, I am querying a device via HTTP GET and getting the following sample JSON response. The response contains as many as 60 or 70 “values” where each value has a unique name, and is structured to 9 different Java class formats (whose class is determined by the “rep” field value). Below is an example with one “value” example for each of six “rep” class =0, 1, 2, 3, 8 and 9.

Does anyone have any tips about how to deserialize such JSON ??

 {
	"totalCount": 6,
	"values": {
		"Pd1774247-7de7-4896-ac76-b7e0dd943c40;0!0083FFFFF00000C": {
			"rep": 0,
			"type": 0,
			"write": false,
			"value": "AAS-20:SU=SiUn;APT=HvacFnct18z_A;APTV=2.003;APS=1;",
			"descriptionName": "ApplicationSoftwareVersion",
			"objectName": "ApplicationSoftwareVersion",
			"memberName": "ApplicationSoftwareVersion",
			"hierarchyName": "ApplicationSoftwareVersion",
			"translated": false
		},
		"Pd1774247-7de7-4896-ac76-b7e0dd943c40;1!00000000E000055": {
			"rep": 1,
			"type": 0,
			"write": false,
			"value": {
				"value": 18.5519028,
				"statusFlags": 0,
				"reliability": 0,
				"eventState": 0,
				"minValue": -50.0,
				"maxValue": 80.0
			},
			"limits": [-50.0, 80.0],
			"descr": "°C",
			"descriptionName": "Outside air temperature",
			"objectName": "R(1)'TOa",
			"memberName": "PresentValue",
			"hierarchyName": "R(1)'FvrBscOp'TOa",
			"translated": false,
			"resolution": 0.5
		},
		"Pd1774247-7de7-4896-ac76-b7e0dd943c40;0!0083FFFFF000077": {
			"rep": 2,
			"type": 0,
			"write": false,
			"value": 0,
			"descriptionName": "UtcOffset",
			"objectName": "UtcOffset",
			"memberName": "UtcOffset",
			"hierarchyName": "UtcOffset",
			"translated": false
		},
		"Pd1774247-7de7-4896-ac76-b7e0dd943c40;0!0083FFFFF0000C4": {
			"rep": 3,
			"type": 0,
			"write": false,
			"value": 0,
			"limits": [0.0, 7.0],
			"descr": "unknown*coldstart*warmstart*detected-power-lost*detected-powered-off*hardware-watchdog*software-watchdog*suspended",
			"descriptionName": "LastRestartReason",
			"objectName": "LastRestartReason",
			"memberName": "LastRestartReason",
			"hierarchyName": "LastRestartReason",
			"translated": false
		},
		"Pd1774247-7de7-4896-ac76-b7e0dd943c40;2!011000004000055": {
			"rep": 8,
			"type": 0,
			"write": true,
			"value": {
				"value": {
					"cmd": "Command",
					"cal": null,
					"act": 0,
					"null": 3,
					"default": 0,
					"schedStart": 4294967295,
					"schedEnd": 4294967295,
					"days": [
						[
							[0, 3],
							[7687, 4],
							[7701, 3]
						],
						[
							[0, 3],
							[7687, 4],
							[7701, 3]
						],
						[
							[0, 3],
							[7687, 4],
							[7701, 3]
						],
						[
							[0, 3],
							[7687, 4],
							[7701, 3]
						],
						[
							[0, 3],
							[7687, 4],
							[7701, 3]
						],
						[
							[0, 3],
							[7687, 4],
							[7701, 3]
						],
						[
							[0, 3],
							[7687, 4],
							[7701, 3]
						],
						[]
					],
					"timeTillNextValue": 632,
					"nextValue": 3
				},
				"statusFlags": 0,
				"reliability": 0
			},
			"limits": [0.0, 4.0],
			"descriptionName": "Room operating mode scheduler",
			"objectName": "R(1)'CenOpMod'ROpModSched",
			"memberName": "PresentValue",
			"hierarchyName": "R(1)'FvrBscOp'ROpModSched",
			"translated": false
		},
		"Pd1774247-7de7-4896-ac76-b7e0dd943c40;3!01100000500004E": {
			"rep": 9,
			"type": 0,
			"write": true,
			"value": {
				"value": {
					"act": 0,
					"rules": [
						[3, 4294967295, 4294967295, 4294967295],
						[3, 4294967295, 4294967295, 4294967295],
						[3, 4294967295, 4294967295, 4294967295],
						[3, 4294967295, 4294967295, 4294967295],
						[3, 4294967295, 4294967295, 4294967295],
						[3, 4294967295, 4294967295, 4294967295],
						[3, 4294967295, 4294967295, 4294967295],
						[3, 4294967295, 4294967295, 4294967295],
						[3, 4294967295, 4294967295, 4294967295],
						[3, 4294967295, 4294967295, 4294967295]
					]
				},
				"statusFlags": 0,
				"reliability": 0
			},
			"descriptionName": "Domestic hot water scheduler",
			"objectName": "R(1)'CenDhw'DhwSched",
			"memberName": "PresentValue",
			"hierarchyName": "R(1)'FvrBscOp'DhwSched",
			"translated": false
		}
	}
}

Use a custom JsonDeserializer - easiest way to handle this.

Many thanks for the tip. I think a custom deserialiser could help create the different classes. However the JSON class “name” of each item is a unique Id (the “Pd177…” in the example below), and I need to find some way to convert that from being a class “name” where each class is uniquely different, to a “class of type rep:0” with a unique Id field of “Pd177…”.

{
		"Pd1774247-7de7-4896-ac76-b7e0dd943c40;0!0083FFFFF00000C": {
			"rep": 0,
			"type": 0,
			"write": false,
			"value": "AAS-20:SU=SiUn;APT=HvacFnct18z_A;APTV=2.003;APS=1;",
			"descriptionName": "ApplicationSoftwareVersion",
			"objectName": "ApplicationSoftwareVersion",
			"memberName": "ApplicationSoftwareVersion",
			"hierarchyName": "ApplicationSoftwareVersion",
			"translated": false
		},

Like this.

1 Like

Here’s an example that I did that is very similar: https://github.com/openhab/openhab2-addons/blob/master/bundles/org.openhab.binding.neeo/src/main/java/org/openhab/binding/neeo/internal/models/NeeoRoomsDeserializer.java

Each parent element was it’s own identifier for my NeeoRoom. The custom deserializer iterates through the array and creates the room from it…

Maybe this could be helpful. http://www.jsonschema2pojo.org/

Yes, very helpful thank you.

Hi Tim, many thanks for the tip.

I solved my problem in three parts as follows…

  1. I parsed the main tree using a Map<String, DataPoint> object, into which GSON can deserialise the respective JSON Data Point elements.

  2. I wrote a base DataPoint class that contains all the common elements of the various JSON variants. And then wrote several additional descendant classes to DataPoint that provide the variations e.g. TextDataPoint, NumericDataPoint, NestedDataPoint, ComplexDataPoint etc.

  3. I wrote a GSON Custom Deserializer (similar to yours) whereby GSON can examine the incoming JSON, and depending on the type and content of some elements, deserialise the JSON into the appropriate DataPoint descendent.

It took me a whole day to write, but it is working very well. :slight_smile:

Many thanks to all.