patternpythonMinor
Recursively generating flat XML elements from hierarchical data structure
Viewed 0 times
elementsxmlrecursivelyflatgeneratinghierarchicalstructurefromdata
Problem
My class has a method that does some stuff, and then wants to recursively operate on some data. Specifically, I'm generating an XML report whose output includes a flat list of elements derived from a hierarchical data structure.
Example Input (a Score)
Desired Output
In JavaScript or Lua I would create a helper function within my method that does the recursion. In Ruby I would create a lambda or proc that does the recursion. Is the 'right' way to do this in Python to create a helper static function? Or is there a better way to keep the original method self-contained, perhaps allowing closures over local variables?
```
class Scorer:
def generate_report(self, score):
r = etree.Element('report', {'user': getpass.getuser(), 'time': timestamp()})
etree.SubElement(r, 'summary', {'tests': score.total(), 'pass': score.passed(),
'skip': score.passed(), 'fail': score.failed()})
etree.SubElement(r, 'results', {'file': self.results_path})
samples = etree.SubElement(r, 'samples')
Scorer._add_score_to_samples(samples, score)
return r
@staticmethod
def _add_score_to_samples(samples, score):
# Some scores are wrappers that should not be included in output
if not score.wrapper:
Example Input (a Score)
{'wrapper':True, 'sub_scores':[
{'wrapper':True, 'sub_scores':[
{'wrapper':False, 'name':'count-objects', 'result':'pass', 'sub_scores':[]},
{'wrapper':False, 'name':'find-object', 'result':'pass', 'sub_scores':[
{'wrapper':False, 'name':'measure.x', 'result':'pass', 'sub_scores':[]},
{'wrapper':False, 'name':'measure.y', 'result':'pass', 'sub_scores':[]},
]},
{'wrapper':False, 'name':'find-object', 'result':'fail', 'sub_scores':[
{'wrapper':False, 'name':'measure.x', 'result':'skip', 'sub_scores':[]},
{'wrapper':False, 'name':'measure.y', 'result':'skip', 'sub_scores':[]},
]}
]}
]}
Desired Output
In JavaScript or Lua I would create a helper function within my method that does the recursion. In Ruby I would create a lambda or proc that does the recursion. Is the 'right' way to do this in Python to create a helper static function? Or is there a better way to keep the original method self-contained, perhaps allowing closures over local variables?
```
class Scorer:
def generate_report(self, score):
r = etree.Element('report', {'user': getpass.getuser(), 'time': timestamp()})
etree.SubElement(r, 'summary', {'tests': score.total(), 'pass': score.passed(),
'skip': score.passed(), 'fail': score.failed()})
etree.SubElement(r, 'results', {'file': self.results_path})
samples = etree.SubElement(r, 'samples')
Scorer._add_score_to_samples(samples, score)
return r
@staticmethod
def _add_score_to_samples(samples, score):
# Some scores are wrappers that should not be included in output
if not score.wrapper:
Solution
I think the code could be made better by keeping all of the XML tree-building code together. The recursive traversal of
Additionally, you might want to reduce the punctuation noise by setting XML attributes using keyword arguments.
Alternatively, you could write
.sub_scores could be done using a generator function.Additionally, you might want to reduce the punctuation noise by setting XML attributes using keyword arguments.
class Scorer:
def generate_report(self, score):
r = etree.Element('report', user=getpass.getuser(), time=timestamp())
etree.SubElement(r, 'summary', tests=score.total(), …)
etree.SubElement(r, 'results', file=self.results_path)
samples = etree.SubElement(r, 'samples')
for sample in self._samples(score):
etree.SubElement(samples, 'sample', sample.report_attr())
return r
def _samples(data):
# Some scores are wrappers that should not be included in output
if not data.wrapper:
yield data
yield from _samples(data.sub_scores)Alternatively, you could write
_samples as a nested function.class Scorer:
def generate_report(self, score):
def extract_samples(data):
# Some scores are wrappers that should not be included in output
if not data.wrapper:
yield data
yield from extract_samples(data.sub_scores)
r = etree.Element('report', user=getpass.getuser(), time=timestamp())
etree.SubElement(r, 'summary', tests=score.total(), …)
etree.SubElement(r, 'results', file=self.results_path)
samples = etree.SubElement(r, 'samples')
for sample in extract_samples(score):
etree.SubElement(samples, 'sample', sample.report_attr())
return rCode Snippets
class Scorer:
def generate_report(self, score):
r = etree.Element('report', user=getpass.getuser(), time=timestamp())
etree.SubElement(r, 'summary', tests=score.total(), …)
etree.SubElement(r, 'results', file=self.results_path)
samples = etree.SubElement(r, 'samples')
for sample in self._samples(score):
etree.SubElement(samples, 'sample', sample.report_attr())
return r
def _samples(data):
# Some scores are wrappers that should not be included in output
if not data.wrapper:
yield data
yield from _samples(data.sub_scores)class Scorer:
def generate_report(self, score):
def extract_samples(data):
# Some scores are wrappers that should not be included in output
if not data.wrapper:
yield data
yield from extract_samples(data.sub_scores)
r = etree.Element('report', user=getpass.getuser(), time=timestamp())
etree.SubElement(r, 'summary', tests=score.total(), …)
etree.SubElement(r, 'results', file=self.results_path)
samples = etree.SubElement(r, 'samples')
for sample in extract_samples(score):
etree.SubElement(samples, 'sample', sample.report_attr())
return rContext
StackExchange Code Review Q#145848, answer score: 2
Revisions (0)
No revisions yet.