Parse "null" string in JSON number field

I am parsing a JSON string. One of the fields is a number, but if unknown contains the string “null”.

I need to populate a Number item Weather_Uvi_BurnTimeCaucasian with the number value or NULL if the JSON returns ‘null’.

How to achieve this?

json (partially):

{"result":{
	"sunTime":{
		"sunrise":"2017-01-27T07:29:59.859Z",
		"sunset":"2017-01-27T16:21:24.397Z"},
		"burnTime":{
			"celtic":null,
			"pale":null,
			"caucasian":null,
			"mediterranean":null,
			"southAfrican":null,
			"negro":null
		},
	}
}

This is the current code in the rule:

rule UvimateUpdated
when
	Item Weather_Uvi_Raw received update
then

	// get values
	val raw        = Weather_Uvi_Raw.state.toString
	val uvi        = transform("JSONPATH", "$.result.uviData.uvi", raw)
	val uviMax     = transform("JSONPATH", "$.result.uviData.uviMax", raw)
	val uviMaxTime = new DateTimeType(transform("JSONPATH", "$.result.uviData.uviMaxTime", raw))

	// need help with processing 'null' value
	val caucasian  = transform("JSONPATH", "$.result.sunTime.burnTime.caucasion", raw)
			
	// post updates
	Weather_Uvi_Uvi.postUpdate(uvi)	
	Weather_Uvi_UviMax.postUpdate(uviMax)	
	Weather_Uvi_UviMaxTime.postUpdate(uviMaxTime)
	Weather_Uvi_BurnTimeCaucasian.postUpdate(caucasian)
	
end

uvi.items

String   Weather_Uvi_Raw
Number   Weather_Uvi_BurnTimeCaucasian
Weather_Uvi_BurnTimeCaucasian.postUpdate(if(caucasian == "null") NULL else caucasian)

This will post “undefined” (i.e. NULL) is caucasian is “null” and the number otherwise.

1

I have tried

	val raw         = Weather_Uvi_Raw.state.toString
	val caucasian   = transform("JSONPATH", "$.result.burnTime.caucasian", raw)
	Weather_Uvi_BurnTimeCaucasian.postUpdate(if (caucasian=="null") NULL else caucasian)

which raises error

18:29:23.215 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'UvimateUpdated': The argument 'state' must not be null or empty.

2

when trying null without quotes

	val raw         = Weather_Uvi_Raw.state.toString
	val caucasian   = transform("JSONPATH", "$.result.burnTime.caucasian", raw)
	Weather_Uvi_BurnTimeCaucasian.postUpdate(if (caucasian==null) NULL else caucasian)

it raises an error, too

18:32:48.076 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'UvimateUpdated': An error occured during the script execution: Could not invoke method: org.eclipse.smarthome.model.script.actions.BusEvent.postUpdate(org.eclipse.smarthome.core.items.Item,java.lang.String) on instance: null

3

when trying NULL instead of null

	val raw         = Weather_Uvi_Raw.state.toString
	val caucasian   = transform("JSONPATH", "$.result.burnTime.caucasian", raw)
	Weather_Uvi_BurnTimeCaucasian.postUpdate(if (caucasian==NULL) NULL else caucasian)

then

18:34:12.204 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'UvimateUpdated': The argument 'state' must not be null or empty.

4

when trying 0 if value is NULL

	val raw         = Weather_Uvi_Raw.state.toString
	val caucasian   = transform("JSONPATH", "$.result.burnTime.caucasian", raw)
	Weather_Uvi_BurnTimeCaucasian.postUpdate(if (caucasian==NULL) 0 else caucasian)

the same error is raised

18:37:12.401 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'UvimateUpdated': The argument 'state' must not be null or empty.

5

When posting the value directly

	val raw         = Weather_Uvi_Raw.state.toString
	val caucasian   = transform("JSONPATH", "$.result.burnTime.caucasian", raw)
	Weather_Uvi_BurnTimeCaucasian.postUpdate(caucasian)

this error too

18:44:07.918 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'UvimateUpdated': The argument 'state' must not be null or empty.

Log out what is actually being returned by the transform so you know what to compare to in the if statement.

rule

	logDebug(logger, caucasian)
	logDebug(logger, "" + caucasian)
	logDebug(logger, "*"+caucasian+"*")

log

18:47:46.755 [DEBUG] [pse.smarthome.model.script.rules.uvi] - 
18:47:46.762 [DEBUG] [pse.smarthome.model.script.rules.uvi] - null
18:47:46.770 [DEBUG] [pse.smarthome.model.script.rules.uvi] - *null*

Should I use if (caucasian=="")?

OK, it looks like the JSONPATH transform is treating "caucasian":null as null (i.e. no value) instead of the String "null".

So the if statement should be:

if(caucasian == null) NULL else caucasian

It can be confusing but null, NULL, and "null" are all different.

So we need to figure out why your 2nd try above failed. Are you certain that Weather_Uvi_BurnTimeCaucasian is identical to how it appears in the .items file?

Have you loaded this rules file into Designer to see if it is highlighting any syntax errors?

You can try to break it down with some more logging to see if something else pops out:

val Number caucasian = transform("JSONPATH", "$.result.burnTime.caucasian", raw)
if(caucasian == null) {
    logInfo("Test", "Caucasian is null!")
    caucasian = 0
}
else {
    logInfo("Test", "Caucasian is " + caucasian)
}
Weather_Uvi_BurnTimeCaucasian.postUpdate(caucasian)
1 Like

This code is working

if (caucasian==null) {
  logInfo(logger, "if")
	Weather_Uvi_BurnTimeCaucasian.postUpdate(NULL)
} else {
  logInfo(logger, "else")
	Weather_Uvi_BurnTimeCaucasian.postUpdate(caucasian)
}

The oneliner still fails

	Weather_Uvi_BurnTimeCaucasian.postUpdate(if (caucasian==null) NULL else caucasian)

Extra parenthesis did not work either

	Weather_Uvi_BurnTimeCaucasian.postUpdate((if (caucasian==null) NULL else caucasian))

An intermediate variable fails, too on the postUpdate, not on the tmp assignment.

  val tmp=(if (caucasian==null) NULL else caucasian)
	Weather_Uvi_BurnTimeCaucasian.postUpdate(tmp)

Weird. I use one liners like that all over the place. I see no reason it shouldn’t work.

This morning I forced caucasian to null (since the Api returned a value at that time). That null seemed to work, and the null from the JSON not. It looked as if in that case the oneliner was working properly. That would imply that the JSONPATH is returning a different null than null. Strange.

I wrote a test rule that shows this. The logInfo for 2a and 2b fail, but the postUpdate succeed. Both logInfo and postUpdate for 2b and 3b fail.

rule Test
when
	Time cron "*/10 * * * * ?" or
	System started
then

	// sample json with numbers
	val rawNumber ='{"result":{"location":"Capelle aan den IJssel","altitude":0,"date":"2017-01-28T16:44:43.452Z","geoLocation":{"geocoding":["Capelle aan den IJssel","ZH","NL"],"lat":"51.91","lng":"4.56"},"uviData":{"uvi":0,"uviMax":0.78,"uviMaxTime":"2017-01-28T11:55:54.462Z"},"uviForecast":[{"date":"2017-01-29T00:00:00.000Z","uviMax":0.89},{"date":"2017-01-31T00:00:00.000Z","uviMax":0.89},{"date":"2017-02-02T00:00:00.000Z","uviMax":0.99},{"date":"2017-02-12T00:00:00.000Z","uviMax":1.31},{"date":"2017-02-27T00:00:00.000Z","uviMax":1.99},{"date":"2017-03-29T00:00:00.000Z","uviMax":4.18}],"sunTime":{"sunrise":"2017-01-28T07:28:35.880Z","sunset":"2017-01-28T16:23:13.044Z"},"burnTime":{"celtic":111,"pale":222,"caucasian":333,"mediterranean":444,"southAfrican":555,"negro":666},"protectionTime":{"protectionFrom":null,"protectionTo":null},"sunAdvice":{"advice":"Use sunscreen with sun protective factor (SPF) 15 or higher, and both UVA and UVB protection"},"weather":{"time":1485621640,"summary":"Overcast","icon":"cloudy","precipIntensity":0,"precipProbability":0,"temperature":6.19,"apparentTemperature":3.03,"dewPoint":1.88,"humidity":0.74,"windSpeed":4.57,"windBearing":196,"visibility":9.99,"cloudCover":1,"pressure":1014.71,"ozone":307.56}}}'

	// sample json with null
	val rawNull ='{"result":{"location":"Capelle aan den IJssel","altitude":0,"date":"2017-01-28T16:44:43.452Z","geoLocation":{"geocoding":["Capelle aan den IJssel","ZH","NL"],"lat":"51.91","lng":"4.56"},"uviData":{"uvi":0,"uviMax":0.78,"uviMaxTime":"2017-01-28T11:55:54.462Z"},"uviForecast":[{"date":"2017-01-29T00:00:00.000Z","uviMax":0.89},{"date":"2017-01-31T00:00:00.000Z","uviMax":0.89},{"date":"2017-02-02T00:00:00.000Z","uviMax":0.99},{"date":"2017-02-12T00:00:00.000Z","uviMax":1.31},{"date":"2017-02-27T00:00:00.000Z","uviMax":1.99},{"date":"2017-03-29T00:00:00.000Z","uviMax":4.18}],"sunTime":{"sunrise":"2017-01-28T07:28:35.880Z","sunset":"2017-01-28T16:23:13.044Z"},"burnTime":{"celtic":null,"pale":null,"caucasian":null,"mediterranean":null,"southAfrican":null,"negro":null},"protectionTime":{"protectionFrom":null,"protectionTo":null},"sunAdvice":{"advice":"Use sunscreen with sun protective factor (SPF) 15 or higher, and both UVA and UVB protection"},"weather":{"time":1485621640,"summary":"Overcast","icon":"cloudy","precipIntensity":0,"precipProbability":0,"temperature":6.19,"apparentTemperature":3.03,"dewPoint":1.88,"humidity":0.74,"windSpeed":4.57,"windBearing":196,"visibility":9.99,"cloudCover":1,"pressure":1014.71,"ozone":307.56}}}'

	val c1   = transform("JSONPATH", "$.result.burnTime.caucasian", rawNumber)
	val c2   = null
	val c3   = transform("JSONPATH", "$.result.burnTime.caucasian", rawNull)

	logInfo("Test", "1")
	logInfo("Test", c1)
	Weather_Uvi_BurnTimeCaucasian.postUpdate(c1)

	logInfo("Test", "2a")
	logInfo("Test", if (c2==null) NULL else c2)
	Weather_Uvi_BurnTimeCaucasian.postUpdate(if (c2==null) NULL else c2)

	logInfo("Test", "3a")
	logInfo("Test", if (c3==null) NULL else c3)
	Weather_Uvi_BurnTimeCaucasian.postUpdate(if (c3==null) NULL else c3)

	logInfo("Test", "2b")
	logInfo("Test", c2)
	Weather_Uvi_BurnTimeCaucasian.postUpdate(c2)

	logInfo("Test", "3b")
	logInfo("Test", c3)
	Weather_Uvi_BurnTimeCaucasian.postUpdate(c3)

end

This is really weird and clearly JSONPATH is doing something odd with null. I’m frag it if ideas. But at least you have a work around.

Yep. I will no longer try the oneliner, but use an if statement.

For future reference, shortest notation:

	if (caucasian==null) Weather_Uvi_BurnTimeCaucasian.postUpdate(NULL)
		else Weather_Uvi_BurnTimeCaucasian.postUpdate(caucasian)

Not sure yet how useful it is to submit an issue somewhere.

I’m not even sure where. Maybe on the JSONPATH transform? Could be a rules issue. Don’t know. It couldn’t hurt to find one though.

I’m not sure. The logInfo("Test", if (c2==null) NULL else c2) raises an error, too. So it’s not just bound to the transform.

I am struggling with the same issue and ran through the whole process like you did.
Do you know if this issue has been solved in OH 2.3?

Hi @NCO
I have come the accross “issue” recently.
The jsonpath transform is the culpript. It will treat a “null” value as null
So use the work around above

I did and it works just fine.
Thanks