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 = '\n- John\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'}}}