Fixes Invalid tag name error when using k:v tagname

This patch fixes the invalid tag name error when k:v type
tagname appears in response of api during xml serialization,
the tagname which contains colon couldn't be supported by
default since it is reserved by xml namespace, this patch
solve it by adding the k of k:v tagname into namespace.

Fixes bug: #1213927

Change-Id: Ib20efb378425cbe5deef45fbac561955cbe0f7bc
This commit is contained in:
guohliu
2013-09-16 19:21:26 +08:00
parent 1533bb2270
commit 80b11279ba
6 changed files with 73 additions and 8 deletions

View File

@@ -32,7 +32,8 @@ authorize = extensions.extension_authorizer('compute', 'flavorextraspecs')
class ExtraSpecsTemplate(xmlutil.TemplateBuilder):
def construct(self):
return xmlutil.MasterTemplate(xmlutil.make_flat_dict('extra_specs'), 1)
extra_specs_dict = xmlutil.make_flat_dict('extra_specs', colon_ns=True)
return xmlutil.MasterTemplate(extra_specs_dict, 1)
class ExtraSpecTemplate(xmlutil.TemplateBuilder):

View File

@@ -28,7 +28,8 @@ from nova.openstack.common.gettextutils import _
class ExtraSpecsTemplate(xmlutil.TemplateBuilder):
def construct(self):
return xmlutil.MasterTemplate(xmlutil.make_flat_dict('extra_specs'), 1)
extra_specs_dict = xmlutil.make_flat_dict('extra_specs', colon_ns=True)
return xmlutil.MasterTemplate(extra_specs_dict, 1)
class ExtraSpecTemplate(xmlutil.TemplateBuilder):

View File

@@ -141,7 +141,7 @@ class TemplateElement(object):
"""Represent an element in the template."""
def __init__(self, tag, attrib=None, selector=None, subselector=None,
**extra):
colon_ns=False, **extra):
"""Initialize an element.
Initializes an element in the template. Keyword arguments
@@ -159,6 +159,9 @@ class TemplateElement(object):
This is used to further refine the datum
object returned by selector in the event
that it is a list of objects.
:colon_ns: An optional flag indicates whether support k:v
type tagname, if so the k:v type tagname will
be supported by adding the k into namespace.
"""
# Convert selector into a Selector
@@ -178,6 +181,7 @@ class TemplateElement(object):
self._text = None
self._children = []
self._childmap = {}
self.colon_ns = colon_ns
# Run the incoming attributes through set() so that they
# become selectorized
@@ -367,6 +371,15 @@ class TemplateElement(object):
tagname = self.tag(datum)
else:
tagname = self.tag
if self.colon_ns:
if ':' in tagname:
if nsmap is None:
nsmap = {}
colon_key, colon_name = tagname.split(':')
nsmap[colon_key] = colon_key
tagname = '{%s}%s' % (colon_key, colon_name)
elem = etree.Element(tagname, nsmap=nsmap)
# If we have a parent, append the node to the parent
@@ -496,7 +509,7 @@ class TemplateElement(object):
def SubTemplateElement(parent, tag, attrib=None, selector=None,
subselector=None, **extra):
subselector=None, colon_ns=False, **extra):
"""Create a template element as a child of another.
Corresponds to the etree.SubElement interface. Parameters are as
@@ -509,7 +522,7 @@ def SubTemplateElement(parent, tag, attrib=None, selector=None,
# Get a TemplateElement
elem = TemplateElement(tag, attrib=attrib, selector=selector,
subselector=subselector)
subselector=subselector, colon_ns=colon_ns)
# Append the parent safely
if parent is not None:
@@ -878,7 +891,8 @@ def make_links(parent, selector=None):
return elem
def make_flat_dict(name, selector=None, subselector=None, ns=None):
def make_flat_dict(name, selector=None, subselector=None,
ns=None, colon_ns=False):
"""
Utility for simple XML templates that traditionally used
XMLDictSerializer with no metadata. Returns a template element
@@ -902,10 +916,11 @@ def make_flat_dict(name, selector=None, subselector=None, ns=None):
# Build the root element
root = TemplateElement(elemname, selector=selector,
subselector=subselector)
subselector=subselector, colon_ns=colon_ns)
# Build an element to represent all the keys and values
elem = SubTemplateElement(root, tagname, selector=get_items)
elem = SubTemplateElement(root, tagname, selector=get_items,
colon_ns=colon_ns)
elem.text = 1
# Return the template

View File

@@ -217,3 +217,13 @@ class FlavorsExtraSpecsXMLSerializerTest(test.TestCase):
'<extra_spec key="key1">value1</extra_spec>')
text = serializer.serialize(dict({"key1": "value1"}))
self.assertEqual(text, expected)
def test_serializer_with_colon_tagname(self):
# Our test object to serialize
obj = {'extra_specs': {'foo:bar': '999'}}
serializer = flavorextraspecs.ExtraSpecsTemplate()
expected_xml = (("<?xml version='1.0' encoding='UTF-8'?>\n"
'<extra_specs><foo:bar xmlns:foo="foo">999</foo:bar>'
'</extra_specs>'))
result = serializer.serialize(obj)
self.assertEqual(expected_xml, result)

View File

@@ -214,3 +214,13 @@ class FlavorsExtraSpecsXMLSerializerTest(test.TestCase):
'<extra_spec key="key1">value1</extra_spec>')
text = serializer.serialize(dict({"key1": "value1"}))
self.assertEqual(text, expected)
def test_serializer_with_colon_tagname(self):
# Our test object to serialize
obj = {'extra_specs': {'foo:bar': '999'}}
serializer = flavors_extraspecs.ExtraSpecsTemplate()
expected_xml = (("<?xml version='1.0' encoding='UTF-8'?>\n"
'<extra_specs><foo:bar xmlns:foo="foo">999</foo:bar>'
'</extra_specs>'))
result = serializer.serialize(obj)
self.assertEqual(expected_xml, result)

View File

@@ -708,6 +708,22 @@ class TemplateTest(test.TestCase):
templ = xmlutil.Template(None)
self.assertEqual(templ.serialize(None), '')
def test_serialize_with_colon_tagname_support(self):
# Our test object to serialize
obj = {'extra_specs': {'foo:bar': '999'}}
expected_xml = (("<?xml version='1.0' encoding='UTF-8'?>\n"
'<extra_specs><foo:bar xmlns:foo="foo">999</foo:bar>'
'</extra_specs>'))
# Set up our master template
root = xmlutil.TemplateElement('extra_specs', selector='extra_specs',
colon_ns=True)
value = xmlutil.SubTemplateElement(root, 'foo:bar', selector='foo:bar',
colon_ns=True)
value.text = xmlutil.Selector()
master = xmlutil.MasterTemplate(root, 1)
result = master.serialize(obj)
self.assertEqual(expected_xml, result)
class MasterTemplateBuilder(xmlutil.TemplateBuilder):
def construct(self):
@@ -800,6 +816,18 @@ class MiscellaneousXMLUtilTests(test.TestCase):
result = tmpl.serialize(dict(wrapper=dict(a='foo', b='bar')))
self.assertEqual(result, expected_xml)
def test_make_flat_dict_with_colon_tagname_support(self):
# Our test object to serialize
obj = {'extra_specs': {'foo:bar': '999'}}
expected_xml = (("<?xml version='1.0' encoding='UTF-8'?>\n"
'<extra_specs><foo:bar xmlns:foo="foo">999</foo:bar>'
'</extra_specs>'))
# Set up our master template
root = xmlutil.make_flat_dict('extra_specs', colon_ns=True)
master = xmlutil.MasterTemplate(root, 1)
result = master.serialize(obj)
self.assertEqual(expected_xml, result)
def test_safe_parse_xml(self):
normal_body = ('<?xml version="1.0" ?>'