From c63f4440de1d981a4501af20920cd22b06193673 Mon Sep 17 00:00:00 2001 From: En3rGy Date: Fri, 17 Mar 2023 22:44:11 +0100 Subject: [PATCH] Fixed https://github.com/En3rGy/11087_JSON-Parser/issues/2 Moved unit test to test dir --- README.md | 18 +++-- config.xml | 2 +- src/11087_JSON_Parser (11087).py | 25 ++++-- src/test_JSON_Parser.py | 134 +++++-------------------------- tests/tst_JSON_Parser.py | 130 ++++++++++++++++++++++++++++++ 5 files changed, 177 insertions(+), 132 deletions(-) create mode 100644 tests/tst_JSON_Parser.py diff --git a/README.md b/README.md index ab25e6b..1fc9bdd 100644 --- a/README.md +++ b/README.md @@ -14,18 +14,18 @@ Beispiel für eine Bausteinkaskade, mit JSON-String = `{"1": "a", "2":[{"2.1": " ## Eingänge -| Nr. | Name | Initialisierung | Beschreibung | -|-----|-------------|-----------------|---------------------------------------------------------------------------------------------| -| 1 | JSON-String | | Gültiger (!) JSON-String | -| 2 | Key | | JSON Key, für den der Wert ausgegeben werden soll.
*Wird nur verwendet, wenn Array Index < 0!* | +| Nr. | Name | Initialisierung | Beschreibung | +|-----|-------------|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------| +| 1 | JSON-String | | Gültiger (!) JSON-String | +| 2 | Key | | JSON Key, für den der Wert ausgegeben werden soll.
*Wird nur verwendet, wenn Array Index < 0!* | | 3 | Array Index | -1 | Index des Array Elements, welches ausgegeben werden soll. Das erste Element hat den Index 0
*Wenn >= 0 wird immer der Index verwendet und nie der Key!* | ## Ausgänge -| Nr. | Name | Initialisierung | Beschreibung | -|-----|----------------|-----------------|--------------------------------------------------| -| 1 | Value (str) | | Angefragter Wert oder JSON-Struktur | -| 2 | Value (number) | 0 | Angefragter Wert als Zahl (float, int oder bool) | +| Nr. | Name | Initialisierung | Beschreibung | +|-----|----------------|-----------------|----------------------------------------------------------------------------------------| +| 1 | Value (str) | | Angefragter Wert oder JSON-Struktur; XML enkodiert, falls UTF-8 Zeichen enthalten sind | +| 2 | Value (number) | 0 | Angefragter Wert als Zahl (float, int oder bool) | ## Sonstiges @@ -34,6 +34,8 @@ Beispiel für eine Bausteinkaskade, mit JSON-String = `{"1": "a", "2":[{"2.1": " ### Change Log +- v1.4 + - XML encoded output if UTF-8 characters would be part of the output - v1.3 - added unit tests - improved reliability / stability diff --git a/config.xml b/config.xml index fefcc5b..8a0b152 100644 --- a/config.xml +++ b/config.xml @@ -1,7 +1,7 @@ - + JSON-String Key diff --git a/src/11087_JSON_Parser (11087).py b/src/11087_JSON_Parser (11087).py index 1463c4f..e289b4d 100644 --- a/src/11087_JSON_Parser (11087).py +++ b/src/11087_JSON_Parser (11087).py @@ -35,23 +35,34 @@ def get_list_element(self, s_json, n_index): return False, "{}" - def get_value(self, s_json, s_key): + def get_value(self, json_file, key): + """ + + :param json_file: + :type json_file: unicode or non-unicode string + :param key: + :type key: non-unicode string + :return: + """ try: - json_file = json.loads(s_json) + json_file = json.loads(json_file) except ValueError as e: - self.DEBUG.add_message('In get_value:129, "' + e.message + '" with\n' + s_json) + self.DEBUG.add_message('In get_value:129, "{}" with \n{}'.format(e.message, json_file)) return False, str() + if not isinstance(key, unicode): + key = key.decode('utf-8') + ret = "" - if s_key in json_file: - val = json_file[s_key] + if key in json_file: + val = json_file[key] if isinstance(val, dict) or isinstance(val, list): ret = json.dumps(val) else: ret = val - if isinstance(ret, str): + if isinstance(ret, unicode): ret = ret.encode("ascii", "xmlcharrefreplace") else: @@ -103,4 +114,4 @@ def on_input_value(self, index, value): except: pass - self._set_output_value(self.PIN_O_SVALUE, str(val)) + self._set_output_value(self.PIN_O_SVALUE, str(val).encode("ascii", "xmlcharrefreplace")) diff --git a/src/test_JSON_Parser.py b/src/test_JSON_Parser.py index 558132b..71348b9 100644 --- a/src/test_JSON_Parser.py +++ b/src/test_JSON_Parser.py @@ -122,23 +122,34 @@ def get_list_element(self, s_json, n_index): return False, "{}" - def get_value(self, s_json, s_key): + def get_value(self, json_file, key): + """ + + :param json_file: + :type json_file: unicode or non-unicode string + :param key: + :type key: non-unicode string + :return: + """ try: - json_file = json.loads(s_json) + json_file = json.loads(json_file) except ValueError as e: - self.DEBUG.add_message('In get_value:129, "' + e.message + '" with\n' + s_json) + self.DEBUG.add_message('In get_value:129, "{}" with \n{}'.format(e.message, json_file)) return False, str() + if not isinstance(key, unicode): + key = key.decode('utf-8') + ret = "" - if s_key in json_file: - val = json_file[s_key] + if key in json_file: + val = json_file[key] if isinstance(val, dict) or isinstance(val, list): ret = json.dumps(val) else: ret = val - if isinstance(ret, str): + if isinstance(ret, unicode): ret = ret.encode("ascii", "xmlcharrefreplace") else: @@ -190,113 +201,4 @@ def on_input_value(self, index, value): except: pass - self._set_output_value(self.PIN_O_SVALUE, str(val)) - - -############################################ - -class JsonTests(unittest.TestCase): - - def setUp(self): - self.dummy = JSON_Parser_11087_11087(0) - self.dummy.on_init() - - def tearDown(self): - pass - - def test_error_json(self): - in_json = '{"test":1, "dummy":2 "go": 3}' - self.dummy.debug_input_value[self.dummy.PIN_I_SJSON] = in_json - self.dummy.debug_input_value[self.dummy.PIN_I_SKEY] = "go" - self.dummy.debug_input_value[self.dummy.PIN_I_NIDX] = -1 - - self.dummy.on_input_value(self.dummy.PIN_I_SKEY, "go") - - self.assertTrue(True) - - def test_getValue_str(self): - in_text = '{"siteCurrentPowerFlow":{"updateRefreshRate":3,"unit":"kW","connections":[{"from":"STORAGE",' \ - '"to":"Load"},{"from":"GRID","to":"Load"}],"GRID":{"status":"Active","currentPower":0.01},' \ - '"LOAD":{"status":"Active","currentPower":1.37},"PV":{"status":"Idle","currentPower":0.0},' \ - '"STORAGE":{"status":"Discharging","currentPower":1.36,"chargeLevel":38,"critical":false}}} ' - - ok, ret = self.dummy.get_value(in_text, "siteCurrentPowerFlow") - res = '{"LOAD": {"status": "Active", "currentPower": 1.37}, "PV": {"status": "Idle", "currentPower": 0.0}, ' \ - '"STORAGE": {"status": "Discharging", "critical": false, "chargeLevel": 38, "currentPower": 1.36}, ' \ - '"connections": [{"to": "Load", "from": "STORAGE"}, {"to": "Load", "from": "GRID"}], "GRID": {"status": ' \ - '"Active", "currentPower": 0.01}, "updateRefreshRate": 3, "unit": "kW"} ' - self.assertTrue(ok) - self.assertEqual(json.loads(ret), json.loads(res)) - - def test_getValue_int(self): - ret = '{"LOAD": {"status": "Active", "currentPower": 1.37}, "PV": {"status": "Idle", "currentPower": 0.0}, ' \ - '"STORAGE": {"status": "Discharging", "critical": false, "chargeLevel": 38, "currentPower": 1.36}, ' \ - '"connections": [{"to": "Load", "from": "STORAGE"}, {"to": "Load", "from": "GRID"}], "GRID": {"status": ' \ - '"Active", "currentPower": 0.01}, "updateRefreshRate": 3, "unit": "kW"} ' - ok, ret = self.dummy.get_value(ret, "updateRefreshRate") - self.assertTrue(ok) - self.assertEqual(ret, 3) - - def test_index(self): - ret = '["LOAD", "Active", "PV"]' - self.dummy.debug_input_value[self.dummy.PIN_I_SJSON] = ret - self.dummy.debug_input_value[self.dummy.PIN_I_SKEY] = str() - self.dummy.debug_input_value[self.dummy.PIN_I_NIDX] = 1 - - self.dummy.on_input_value(self.dummy.PIN_I_NIDX, 1) - - ret = self.dummy.debug_output_value[self.dummy.PIN_O_SVALUE] - self.assertEqual(ret, 'Active') - - ret = '[["LOAD", "Active", "PV"], [1, 2, 3]]' - self.dummy.debug_input_value[self.dummy.PIN_I_SJSON] = ret - - self.dummy.on_input_value(self.dummy.PIN_I_NIDX, 1) - - ret = self.dummy.debug_output_value[self.dummy.PIN_O_SVALUE] - self.assertEqual(ret, "[1, 2, 3]") - - def test_key(self): - ret = '{"LOAD": {"status": "Active", "currentPower": 1.37}, "PV": {"status": "Idle", "currentPower": 0.0}, ' \ - '"STORAGE": {"status": "Discharging", "critical": false, "chargeLevel": 38, "currentPower": 1.36}, ' \ - '"connections": [{"to": "Load", "from": "STORAGE"}, {"to": "Load", "from": "GRID"}], "GRID": {"status": ' \ - '"Active", "currentPower": 0.01}, "updateRefreshRate": 3, "unit": "kW"} ' - - self.dummy.debug_input_value[self.dummy.PIN_I_SJSON] = ret - self.dummy.debug_input_value[self.dummy.PIN_I_SKEY] = "updateRefreshRate" - self.dummy.debug_input_value[self.dummy.PIN_I_NIDX] = -1 - - self.dummy.on_input_value(self.dummy.PIN_I_NIDX, -1) - - ret = self.dummy.debug_output_value[self.dummy.PIN_O_SVALUE] - self.assertEqual(ret, "3") - - ret = self.dummy.debug_output_value[self.dummy.PIN_O_FVALUE] - self.assertEqual(ret, 3) - - def test_kaskade(self): - ret = '{"1": "a", "2":[{"2.1": "b.1", "2.2": "b.2"}, {"2.3": "b.3"}]}' - self.dummy.debug_input_value[self.dummy.PIN_I_SJSON] = ret - self.dummy.debug_input_value[self.dummy.PIN_I_NIDX] = -1 - self.dummy.debug_input_value[self.dummy.PIN_I_SKEY] = "2" - - self.dummy.on_input_value(self.dummy.PIN_I_SKEY, "2") - ret = self.dummy.debug_output_value[self.dummy.PIN_O_SVALUE] - self.assertEqual(json.loads('[{"2.1": "b.1", "2.2": "b.2"}, {"2.3": "b.3"}]'), json.loads(ret)) - - self.dummy.debug_input_value[self.dummy.PIN_I_SJSON] = ret - self.dummy.debug_input_value[self.dummy.PIN_I_NIDX] = 0 - self.dummy.on_input_value(self.dummy.PIN_I_NIDX, 0) - ret = self.dummy.debug_output_value[self.dummy.PIN_O_SVALUE] - self.assertEqual(json.loads('{"2.1": "b.1", "2.2": "b.2"}'), json.loads(ret)) - - self.dummy.debug_input_value[self.dummy.PIN_I_SJSON] = ret - self.dummy.debug_input_value[self.dummy.PIN_I_NIDX] = -1 - self.dummy.debug_input_value[self.dummy.PIN_I_SKEY] = "2.2" - self.dummy.on_input_value(self.dummy.PIN_I_SKEY, "2.2") - ret = self.dummy.debug_output_value[self.dummy.PIN_O_SVALUE] - - self.assertEqual("b.2", ret) - -if __name__ == '__main__': - unittest.main() + self._set_output_value(self.PIN_O_SVALUE, str(val).encode("ascii", "xmlcharrefreplace")) diff --git a/tests/tst_JSON_Parser.py b/tests/tst_JSON_Parser.py new file mode 100644 index 0000000..95cc8f0 --- /dev/null +++ b/tests/tst_JSON_Parser.py @@ -0,0 +1,130 @@ +# coding: utf8 + +import unittest +import time +import json +import random + +from test_JSON_Parser import JSON_Parser_11087_11087 + + +class JsonTests(unittest.TestCase): + + def setUp(self): + self.dummy = JSON_Parser_11087_11087(0) + self.dummy.on_init() + + def tearDown(self): + pass + + def test_error_json(self): + in_json = '{"test":1, "dummy":2 "go": 3}' + self.dummy.debug_input_value[self.dummy.PIN_I_SJSON] = in_json + self.dummy.debug_input_value[self.dummy.PIN_I_SKEY] = "go" + self.dummy.debug_input_value[self.dummy.PIN_I_NIDX] = -1 + + self.dummy.on_input_value(self.dummy.PIN_I_SKEY, "go") + + self.assertTrue(True) + + def test_getValue_str(self): + in_text = '{"siteCurrentPowerFlow":{"updateRefreshRate":3,"unit":"kW","connections":[{"from":"STORAGE",' \ + '"to":"Load"},{"from":"GRID","to":"Load"}],"GRID":{"status":"Active","currentPower":0.01},' \ + '"LOAD":{"status":"Active","currentPower":1.37},"PV":{"status":"Idle","currentPower":0.0},' \ + '"STORAGE":{"status":"Discharging","currentPower":1.36,"chargeLevel":38,"critical":false}}} ' + + ok, ret = self.dummy.get_value(in_text, "siteCurrentPowerFlow") + res = '{"LOAD": {"status": "Active", "currentPower": 1.37}, "PV": {"status": "Idle", "currentPower": 0.0}, ' \ + '"STORAGE": {"status": "Discharging", "critical": false, "chargeLevel": 38, "currentPower": 1.36}, ' \ + '"connections": [{"to": "Load", "from": "STORAGE"}, {"to": "Load", "from": "GRID"}], "GRID": {"status": ' \ + '"Active", "currentPower": 0.01}, "updateRefreshRate": 3, "unit": "kW"} ' + self.assertTrue(ok) + self.assertEqual(json.loads(ret), json.loads(res)) + + def test_getValue_int(self): + ret = '{"LOAD": {"status": "Active", "currentPower": 1.37}, "PV": {"status": "Idle", "currentPower": 0.0}, ' \ + '"STORAGE": {"status": "Discharging", "critical": false, "chargeLevel": 38, "currentPower": 1.36}, ' \ + '"connections": [{"to": "Load", "from": "STORAGE"}, {"to": "Load", "from": "GRID"}], "GRID": {"status": ' \ + '"Active", "currentPower": 0.01}, "updateRefreshRate": 3, "unit": "kW"} ' + ok, ret = self.dummy.get_value(ret, "updateRefreshRate") + self.assertTrue(ok) + self.assertEqual(ret, 3) + + def test_index(self): + ret = '["LOAD", "Active", "PV"]' + self.dummy.debug_input_value[self.dummy.PIN_I_SJSON] = ret + self.dummy.debug_input_value[self.dummy.PIN_I_SKEY] = str() + self.dummy.debug_input_value[self.dummy.PIN_I_NIDX] = 1 + + self.dummy.on_input_value(self.dummy.PIN_I_NIDX, 1) + + ret = self.dummy.debug_output_value[self.dummy.PIN_O_SVALUE] + self.assertEqual(ret, 'Active') + + ret = '[["LOAD", "Active", "PV"], [1, 2, 3]]' + self.dummy.debug_input_value[self.dummy.PIN_I_SJSON] = ret + + self.dummy.on_input_value(self.dummy.PIN_I_NIDX, 1) + + ret = self.dummy.debug_output_value[self.dummy.PIN_O_SVALUE] + self.assertEqual(ret, "[1, 2, 3]") + + def test_key(self): + ret = '{"LOAD": {"status": "Active", "currentPower": 1.37}, "PV": {"status": "Idle", "currentPower": 0.0}, ' \ + '"STORAGE": {"status": "Discharging", "critical": false, "chargeLevel": 38, "currentPower": 1.36}, ' \ + '"connections": [{"to": "Load", "from": "STORAGE"}, {"to": "Load", "from": "GRID"}], "GRID": {"status": ' \ + '"Active", "currentPower": 0.01}, "updateRefreshRate": 3, "unit": "kW"} ' + + self.dummy.debug_input_value[self.dummy.PIN_I_SJSON] = ret + self.dummy.debug_input_value[self.dummy.PIN_I_SKEY] = "updateRefreshRate" + self.dummy.debug_input_value[self.dummy.PIN_I_NIDX] = -1 + + self.dummy.on_input_value(self.dummy.PIN_I_NIDX, -1) + + ret = self.dummy.debug_output_value[self.dummy.PIN_O_SVALUE] + self.assertEqual(ret, "3") + + ret = self.dummy.debug_output_value[self.dummy.PIN_O_FVALUE] + self.assertEqual(ret, 3) + + def test_kaskade(self): + ret = '{"1": "a", "2":[{"2.1": "b.1", "2.2": "b.2"}, {"2.3": "b.3"}]}' + self.dummy.debug_input_value[self.dummy.PIN_I_SJSON] = ret + self.dummy.debug_input_value[self.dummy.PIN_I_NIDX] = -1 + self.dummy.debug_input_value[self.dummy.PIN_I_SKEY] = "2" + + self.dummy.on_input_value(self.dummy.PIN_I_SKEY, "2") + ret = self.dummy.debug_output_value[self.dummy.PIN_O_SVALUE] + self.assertEqual(json.loads('[{"2.1": "b.1", "2.2": "b.2"}, {"2.3": "b.3"}]'), json.loads(ret)) + + self.dummy.debug_input_value[self.dummy.PIN_I_SJSON] = ret + self.dummy.debug_input_value[self.dummy.PIN_I_NIDX] = 0 + self.dummy.on_input_value(self.dummy.PIN_I_NIDX, 0) + ret = self.dummy.debug_output_value[self.dummy.PIN_O_SVALUE] + self.assertEqual(json.loads('{"2.1": "b.1", "2.2": "b.2"}'), json.loads(ret)) + + self.dummy.debug_input_value[self.dummy.PIN_I_SJSON] = ret + self.dummy.debug_input_value[self.dummy.PIN_I_NIDX] = -1 + self.dummy.debug_input_value[self.dummy.PIN_I_SKEY] = "2.2" + self.dummy.on_input_value(self.dummy.PIN_I_SKEY, "2.2") + ret = self.dummy.debug_output_value[self.dummy.PIN_O_SVALUE] + + self.assertEqual("b.2", ret) + + def test_unicode(self): + in_val = u'{"2.1": "Äö", "2.2": "b.2"}' + + ok, ret = self.dummy.get_value(in_val, "2.1") + res = 'Äö' + self.assertTrue(ok) + self.assertEqual(ret, res) + ok, ret = self.dummy.get_value(in_val, u"2.1") + self.assertTrue(ok) + self.assertEqual(ret, res) + ok, ret = self.dummy.get_value(in_val, u"2.2") + self.assertTrue(ok) + self.assertEqual(ret, "b.2") + + +if __name__ == '__main__': + unittest.main()