construct: Recursive normalization of construct parse result
If we want to use construct parse results to generate JSON serializable
dicts, we need to
* apply the filter_dict() operation recursively, and
* simplify the construct Container and ListContainer classes to
a simple dict and/or list.
We introduce a pySim.construct.parse_construct() helper which is
subsequently used from all pySim.filesystem caller sites.
Change-Id: I319414eb69808ef65895293832bb30519f45949d
diff --git a/pySim/construct.py b/pySim/construct.py
index d0101e3..2a3efd3 100644
--- a/pySim/construct.py
+++ b/pySim/construct.py
@@ -1,3 +1,4 @@
+import typing
from construct import *
from pySim.utils import b2h, h2b, swap_nibbles
import gsm0338
@@ -84,6 +85,34 @@
res[key] = value
return res
+from construct.lib.containers import Container, ListContainer
+from construct.core import EnumIntegerString
+
+def normalize_construct(c):
+ """Convert a construct specific type to a related base type, mostly useful
+ so we can serialize it."""
+ # we need to include the filter_dict as we otherwise get elements like this
+ # in the dict: '_io': <_io.BytesIO object at 0x7fdb64e05860> which we cannot json-serialize
+ c = filter_dict(c)
+ if isinstance(c, Container) or isinstance(c, dict):
+ r = {k : normalize_construct(v) for (k, v) in c.items()}
+ elif isinstance(c, ListContainer):
+ r = [normalize_construct(x) for x in c]
+ elif isinstance(c, list):
+ r = [normalize_construct(x) for x in c]
+ elif isinstance(c, EnumIntegerString):
+ r = str(c)
+ else:
+ r = c
+ return r
+
+def parse_construct(c, raw_bin_data:bytes, length:typing.Optional[int]=None, exclude_prefix:str='_'):
+ """Helper function to wrap around normalize_construct() and filter_dict()."""
+ if not length:
+ length = len(raw_bin_data)
+ parsed = c.parse(raw_bin_data, total_len=length)
+ return normalize_construct(parsed)
+
# here we collect some shared / common definitions of data types
LV = Prefixed(Int8ub, HexAdapter(GreedyBytes))