diff --git a/scrapy/contrib/exporter/__init__.py b/scrapy/contrib/exporter/__init__.py index 685ab35da..ab0e9a94f 100644 --- a/scrapy/contrib/exporter/__init__.py +++ b/scrapy/contrib/exporter/__init__.py @@ -134,7 +134,10 @@ class XmlItemExporter(BaseItemExporter): def _export_xml_field(self, name, serialized_value): self.xg.startElement(name, {}) - if hasattr(serialized_value, '__iter__'): + if hasattr(serialized_value, 'items'): + for subname, value in serialized_value.items(): + self._export_xml_field(subname, value) + elif hasattr(serialized_value, '__iter__'): for value in serialized_value: self._export_xml_field('value', value) else: diff --git a/scrapy/tests/test_contrib_exporter.py b/scrapy/tests/test_contrib_exporter.py index e5c03c87a..d4865aafa 100644 --- a/scrapy/tests/test_contrib_exporter.py +++ b/scrapy/tests/test_contrib_exporter.py @@ -171,6 +171,47 @@ class XmlItemExporterTest(BaseItemExporterTest): expected_value = '\nJohn\xc2\xa3Doe' self.assertEqual(output.getvalue(), expected_value) + def test_nested_item(self): + output = StringIO() + i1 = TestItem(name=u'foo\xa3hoo', age='22') + i2 = TestItem(name=u'bar', age=i1) + i3 = TestItem(name=u'buz', age=i2) + ie = XmlItemExporter(output) + ie.start_exporting() + ie.export_item(i3) + ie.finish_exporting() + expected_value = '\n'\ + ''\ + ''\ + ''\ + '22'\ + 'foo\xc2\xa3hoo'\ + ''\ + 'bar'\ + ''\ + 'buz'\ + '' + self.assertEqual(output.getvalue(), expected_value) + + def test_nested_list_item(self): + output = StringIO() + i1 = TestItem(name=u'foo') + i2 = TestItem(name=u'bar') + i3 = TestItem(name=u'buz', age=[i1, i2]) + ie = XmlItemExporter(output) + ie.start_exporting() + ie.export_item(i3) + ie.finish_exporting() + expected_value = '\n'\ + ''\ + ''\ + 'foo'\ + 'bar'\ + ''\ + 'buz'\ + '' + self.assertEqual(output.getvalue(), expected_value) + class JsonLinesItemExporterTest(BaseItemExporterTest): _expected_nested = {'name': u'Jesus', 'age': {'name': 'Maria', 'age': {'name': 'Joseph', 'age': '22'}}}