diff options
| author | Tim Graham <timograham@gmail.com> | 2026-04-04 19:16:41 -0400 |
|---|---|---|
| committer | Jacob Walls <jacobtylerwalls@gmail.com> | 2026-04-19 13:38:12 +0300 |
| commit | 3af5cb17b83eaaea6d4def494269694c009d89c5 (patch) | |
| tree | 67d0fd57ae111ca35062aa15bf76d00f9521ca30 | |
| parent | 8ddc5b444c175c696c8197dc8f24273252b0de77 (diff) | |
Added support for nested fields to XML deserializer.
Needed by Django MongoDB Backend's EmbeddedModelField.
| -rw-r--r-- | django/core/serializers/xml_serializer.py | 45 |
1 files changed, 32 insertions, 13 deletions
diff --git a/django/core/serializers/xml_serializer.py b/django/core/serializers/xml_serializer.py index 5dfbb76a01..6d3005ac40 100644 --- a/django/core/serializers/xml_serializer.py +++ b/django/core/serializers/xml_serializer.py @@ -292,7 +292,7 @@ class Deserializer(base.Deserializer): field_names = {f.name for f in Model._meta.get_fields()} # Deserialize each field. - for field_node in node.getElementsByTagName("field"): + for field_node in getChildrenByTagName(node, "field"): # If the field is missing the name attribute, bail (are you # sensing a pattern here?) field_name = field_node.getAttribute("name") @@ -319,12 +319,12 @@ class Deserializer(base.Deserializer): [ ( None - if nat_node.getElementsByTagName("None") + if getChildrenByTagName(nat_node, "None") else getInnerText(nat_node).strip() ) - for nat_node in obj_node.getElementsByTagName("natural") + for nat_node in getChildrenByTagName(obj_node, "natural") ] - for obj_node in field_node.getElementsByTagName("object") + for obj_node in getChildrenByTagName(field_node, "object") ] else: m2m_data[field.name] = value @@ -336,15 +336,15 @@ class Deserializer(base.Deserializer): deferred_fields[field] = [ ( None - if k.getElementsByTagName("None") + if getChildrenByTagName(k, "None") else getInnerText(k).strip() ) - for k in field_node.getElementsByTagName("natural") + for k in getChildrenByTagName(field_node, "natural") ] else: data[field.attname] = value else: - if field_node.getElementsByTagName("None"): + if getChildrenByTagName(field_node, "None"): value = None else: value = field.to_python(getInnerText(field_node).strip()) @@ -363,8 +363,8 @@ class Deserializer(base.Deserializer): Handle a <field> node for a ForeignKey """ # Check if there is a child node named 'None', returning None if so. - natural_keys = node.getElementsByTagName("natural") - if node.getElementsByTagName("None") and not natural_keys: + natural_keys = getChildrenByTagName(node, "natural") + if getChildrenByTagName(node, "None") and not natural_keys: return None else: model = field.remote_field.model @@ -374,7 +374,7 @@ class Deserializer(base.Deserializer): # key field_value = [] for k in natural_keys: - if k.getElementsByTagName("None"): + if getChildrenByTagName(k, "None"): field_value.append(None) else: field_value.append(getInnerText(k).strip()) @@ -414,13 +414,13 @@ class Deserializer(base.Deserializer): if hasattr(default_manager, "get_by_natural_key"): def m2m_convert(n): - keys = n.getElementsByTagName("natural") + keys = getChildrenByTagName(n, "natural") if keys: # If there are 'natural' subelements, it must be a natural # key field_value = [] for k in keys: - if k.getElementsByTagName("None"): + if getChildrenByTagName(k, "None"): field_value.append(None) else: field_value.append(getInnerText(k).strip()) @@ -441,7 +441,7 @@ class Deserializer(base.Deserializer): values = [] try: - for c in node.getElementsByTagName("object"): + for c in getChildrenByTagName(node, "object"): values.append(m2m_convert(c)) except SuspiciousOperation: raise @@ -479,6 +479,25 @@ def check_element_type(element): return element.nodeType in (element.TEXT_NODE, element.CDATA_SECTION_NODE) +def getChildrenByTagName(node, tag_name): + """ + Like Element.getElementsByTagName() but return only direct children. + + Element.getElementsByTagName() searches all descendants (direct children, + children's children, etc.). This prevents correct deserialization of + third-party nested fields. For example: + + <field name="author" type="EmbeddedModelField"> + <object model="app.Model"> + <field name="id" type="..."><None></None></field> + """ + return [ + n + for n in node.childNodes + if n.nodeType == node.ELEMENT_NODE and n.tagName == tag_name + ] + + def getInnerText(node): return "".join( [child.data for child in node.childNodes if check_element_type(child)] |
