Mercurial > hg > ooxml
changeset 71:54bb53434887
begin work on decoder that allows identifiers as keys and values
author | Henry S. Thompson <ht@markup.co.uk> |
---|---|
date | Wed, 28 Jun 2017 19:08:30 +0100 |
parents | 0003fe7b6b67 |
children | 0654e37583b5 |
files | eDecoder.py excel.py sample2y.xlsx |
diffstat | 3 files changed, 164 insertions(+), 10 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eDecoder.py Wed Jun 28 19:08:30 2017 +0100 @@ -0,0 +1,104 @@ +'''Extend JSON to support atoms as properties and class names''' +from json import decoder +from json.decoder import JSONDecoder, JSONObject, _CONSTANTS, WHITESPACE, WHITESPACE_STR, FLAGS, errmsg + +import json, re + +JSONObject_orig=JSONObject + +class eDecoder(JSONDecoder): + def __init__(self, encoding=None, object_hook=None, parse_float=None, + parse_int=None, parse_constant=None, strict=True, + object_pairs_hook=None): + + self.parse_object = eJSONObject + super(eDecoder,self).__init__(encoding, object_hook, parse_float, + parse_int, e_parse_constant, strict, + object_pairs_hook) + +def e_parse_constant(istr): + print('constant: %s'%istr) + return _CONSTANTS[istr] + +IDENT=re.compile(r'[a-zA-Z0-9_.-]+', FLAGS) + +def eJSONObject(s_and_end, encoding, strict, scan_once, object_hook, + object_pairs_hook, _w=WHITESPACE.match, _ws=WHITESPACE_STR): + s, end = s_and_end + if (s[end] in ('"','}') or + (s[end] in _ws and + s[_w(s,end).end()] in ('"','}'))): + return JSONObject_orig(s_and_end, encoding, strict, scan_once, object_hook, + object_pairs_hook, _w, _ws) + while True: + #key, end = scanstring(s, end, encoding, strict) + isId = IDENT.match(s,end) + if isId: + key=isId.group() + end=isId.end() + else: + raise ValueError(errmsg( + "Expecting property name with or w/o in double quotes", s, end)) + + # To skip some function call overhead we optimize the fast paths where + # the JSON key separator is ": " or just ":". + if s[end:end + 1] != ':': + end = _w(s, end).end() + if s[end:end + 1] != ':': + raise ValueError(errmsg("Expecting ':' delimiter", s, end)) + end += 1 + + try: + if s[end] in _ws: + end += 1 + if s[end] in _ws: + end = _w(s, end + 1).end() + except IndexError: + pass + + try: + value, end = scan_once(s, end) + except StopIteration: + raise ValueError(errmsg("Expecting object", s, end)) + pairs_append((key, value)) + + try: + nextchar = s[end] + if nextchar in _ws: + end = _w(s, end + 1).end() + nextchar = s[end] + except IndexError: + nextchar = '' + end += 1 + + if nextchar == '}': + break + elif nextchar != ',': + raise ValueError(errmsg("Expecting ',' delimiter", s, end - 1)) + + try: + nextchar = s[end] + if nextchar in _ws: + end += 1 + nextchar = s[end] + if nextchar in _ws: + end = _w(s, end + 1).end() + nextchar = s[end] + except IndexError: + nextchar = '' + + end += 1 + if nextchar != '"': + raise ValueError(errmsg( + "Expecting property name enclosed in double quotes", s, end - 1)) + if object_pairs_hook is not None: + result = object_pairs_hook(pairs) + return result, end + pairs = dict(pairs) + if object_hook is not None: + pairs = object_hook(pairs) + return pairs, end + + +json.scanner.make_scanner=json.scanner.py_make_scanner +json.decoder.JSONObject=eJSONObject
--- a/excel.py Mon Jun 26 18:08:25 2017 +0100 +++ b/excel.py Wed Jun 28 19:08:30 2017 +0100 @@ -1,11 +1,16 @@ #!/usr/bin/python3 '''Class model for analysis of Excel spreadsheets''' from jsonweb.encode import to_object, dumper +from jsonweb.decode import from_object, loader +import json +import eDecoder + try: string_types=basestring except NameError: string_types=str +@from_object() @to_object(exclude_nulls=True) class Book(object): def __init__(self,source,sheets=[],formats=[]): @@ -21,15 +26,14 @@ assert(isinstance(sheet,Sheet)) self.sheets.append(sheet) +@from_object() @to_object(exclude_nulls=True,suppress=["book"]) class Sheet(object): - def __init__(self,name,book,tables=[],docs=[],misc=[]): + def __init__(self,name,book=None,tables=[],docs=[],misc=[]): assert(isinstance(name,string_types)) self.name=name - assert(isinstance(book,Book)) - self.book=book tables=list(tables) - assert(all(isinstance(s,Region) for s in tables)) + assert(all(isinstance(s,Table) for s in tables)) self.tables=tables docs=list(docs) assert(all(isinstance(s,Region) for s in docs)) @@ -37,22 +41,68 @@ misc=list(misc) assert(all(isinstance(s,Region) for s in misc)) self.misc=misc - book.addSheet(self) + if book is not None: + assert(isinstance(book,Book)) + book.addSheet(self) + self.book=book def addTable(self,table): assert(isinstance(table,Region)) self.tables.append(table) -@to_object(exclude_nulls=True,suppress=["sheet"]) +@from_object() +@to_object(exclude_nulls=True,suppress=["parent"]) class Region(object): - def __init__(self,name,sheet=None,ranges=[]): - if sheet is not None: - assert(isinstance(sheet,Sheet)) - self.sheet=sheet + def __init__(self,name,parent=None,ranges=[],content=[]): + if parent is not None: + assert(isinstance(parent,(Region,Sheet))) + self.parent=parent ranges=list(ranges) assert(all(isinstance(s,Range) for s in ranges)) self.ranges=ranges + content=list(content) + assert(all(isinstance(s,(Region,string_types)) for s in content)) + self.content=content assert(isinstance(name,string_types)) self.name=name + if parent is not None: + parent.addRegion(self) + + def addRegion(self,content): + assert(isinstance(content,(Region,string_types))) + self.content.append(content) + +@from_object() +@to_object(exclude_nulls=True,suppress=["sheet"]) +class Table(Region): + def __init__(self,name,sheet=None,ranges=[],labels=[],data=[]): + super(Table,self).__init__(name,sheet,ranges) + labels=list(labels) + assert(all(isinstance(s,Label) for s in labels)) + self.labels=labels + data=list(data) + assert(all(isinstance(s,Region) for s in data)) + self.data=data if sheet is not None: + assert(isinstance(sheet,Sheet)) sheet.addTable(self) + self.sheet=sheet + + def addLabel(self,label): + assert(isinstance(label,Label)) + self.labels.append(label) + + def addData(self,data): + assert(isinstance(data,Data)) + self.data.append(data) + + def addRegion(self,region): + assert(isinstance(region,(Label,Data))) + if isinstance(region,Label): + self.addLabel(region) + else: + self.addData(region) + +def lt(filename): + with open(filename,'r') as js: + return json.loads(js.read(),cls=eDecoder.eDecoder)