Skip to content

Commit 3af5cb1

Browse files
timgrahamjacobtylerwalls
authored andcommitted
Added support for nested fields to XML deserializer.
Needed by Django MongoDB Backend's EmbeddedModelField.
1 parent 8ddc5b4 commit 3af5cb1

1 file changed

Lines changed: 32 additions & 13 deletions

File tree

django/core/serializers/xml_serializer.py

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ def _handle_object(self, node):
292292

293293
field_names = {f.name for f in Model._meta.get_fields()}
294294
# Deserialize each field.
295-
for field_node in node.getElementsByTagName("field"):
295+
for field_node in getChildrenByTagName(node, "field"):
296296
# If the field is missing the name attribute, bail (are you
297297
# sensing a pattern here?)
298298
field_name = field_node.getAttribute("name")
@@ -319,12 +319,12 @@ def _handle_object(self, node):
319319
[
320320
(
321321
None
322-
if nat_node.getElementsByTagName("None")
322+
if getChildrenByTagName(nat_node, "None")
323323
else getInnerText(nat_node).strip()
324324
)
325-
for nat_node in obj_node.getElementsByTagName("natural")
325+
for nat_node in getChildrenByTagName(obj_node, "natural")
326326
]
327-
for obj_node in field_node.getElementsByTagName("object")
327+
for obj_node in getChildrenByTagName(field_node, "object")
328328
]
329329
else:
330330
m2m_data[field.name] = value
@@ -336,15 +336,15 @@ def _handle_object(self, node):
336336
deferred_fields[field] = [
337337
(
338338
None
339-
if k.getElementsByTagName("None")
339+
if getChildrenByTagName(k, "None")
340340
else getInnerText(k).strip()
341341
)
342-
for k in field_node.getElementsByTagName("natural")
342+
for k in getChildrenByTagName(field_node, "natural")
343343
]
344344
else:
345345
data[field.attname] = value
346346
else:
347-
if field_node.getElementsByTagName("None"):
347+
if getChildrenByTagName(field_node, "None"):
348348
value = None
349349
else:
350350
value = field.to_python(getInnerText(field_node).strip())
@@ -363,8 +363,8 @@ def _handle_fk_field_node(self, node, field):
363363
Handle a <field> node for a ForeignKey
364364
"""
365365
# Check if there is a child node named 'None', returning None if so.
366-
natural_keys = node.getElementsByTagName("natural")
367-
if node.getElementsByTagName("None") and not natural_keys:
366+
natural_keys = getChildrenByTagName(node, "natural")
367+
if getChildrenByTagName(node, "None") and not natural_keys:
368368
return None
369369
else:
370370
model = field.remote_field.model
@@ -374,7 +374,7 @@ def _handle_fk_field_node(self, node, field):
374374
# key
375375
field_value = []
376376
for k in natural_keys:
377-
if k.getElementsByTagName("None"):
377+
if getChildrenByTagName(k, "None"):
378378
field_value.append(None)
379379
else:
380380
field_value.append(getInnerText(k).strip())
@@ -414,13 +414,13 @@ def _handle_m2m_field_node(self, node, field):
414414
if hasattr(default_manager, "get_by_natural_key"):
415415

416416
def m2m_convert(n):
417-
keys = n.getElementsByTagName("natural")
417+
keys = getChildrenByTagName(n, "natural")
418418
if keys:
419419
# If there are 'natural' subelements, it must be a natural
420420
# key
421421
field_value = []
422422
for k in keys:
423-
if k.getElementsByTagName("None"):
423+
if getChildrenByTagName(k, "None"):
424424
field_value.append(None)
425425
else:
426426
field_value.append(getInnerText(k).strip())
@@ -441,7 +441,7 @@ def m2m_convert(n):
441441

442442
values = []
443443
try:
444-
for c in node.getElementsByTagName("object"):
444+
for c in getChildrenByTagName(node, "object"):
445445
values.append(m2m_convert(c))
446446
except SuspiciousOperation:
447447
raise
@@ -479,6 +479,25 @@ def check_element_type(element):
479479
return element.nodeType in (element.TEXT_NODE, element.CDATA_SECTION_NODE)
480480

481481

482+
def getChildrenByTagName(node, tag_name):
483+
"""
484+
Like Element.getElementsByTagName() but return only direct children.
485+
486+
Element.getElementsByTagName() searches all descendants (direct children,
487+
children's children, etc.). This prevents correct deserialization of
488+
third-party nested fields. For example:
489+
490+
<field name="author" type="EmbeddedModelField">
491+
<object model="app.Model">
492+
<field name="id" type="..."><None></None></field>
493+
"""
494+
return [
495+
n
496+
for n in node.childNodes
497+
if n.nodeType == node.ELEMENT_NODE and n.tagName == tag_name
498+
]
499+
500+
482501
def getInnerText(node):
483502
return "".join(
484503
[child.data for child in node.childNodes if check_element_type(child)]

0 commit comments

Comments
 (0)