Skip to content

Fix XML namespace serialization to only add xmlns when needed#3

Open
mischadiehm wants to merge 1 commit into
pydantify:mainfrom
mischadiehm:feature/xml-namespace-fix
Open

Fix XML namespace serialization to only add xmlns when needed#3
mischadiehm wants to merge 1 commit into
pydantify:mainfrom
mischadiehm:feature/xml-namespace-fix

Conversation

@mischadiehm
Copy link
Copy Markdown

Summary

  • Fix fields_to_elements() to only add xmlns declaration when namespace differs from parent
  • Implement data_root wrapper in model_dump_xml_string() for NETCONF base namespace
  • Use __class__.model_fields to avoid Pydantic deprecation warnings

Problem

The current implementation incorrectly adds xmlns declarations on every element. It should only add xmlns when the child's namespace differs from the parent's.

Solution

Added parent_namespace parameter to fields_to_elements():

  • Only add nsmap={None: self.namespace} when parent_namespace is None or self.namespace != parent_namespace
  • Pass parent_namespace=self.namespace in all recursive calls

Test plan

  • test_augment_xml_dump - validates different-namespace behavior (xmlns on each element where namespace changes)
  • test_import_uses_xml_dump - validates same-namespace behavior (xmlns only on root, children inherit)
  • All tests pass
  • Ruff lint passes

🤖 Generated with Claude Code

The fields_to_elements method now accepts a parent_namespace parameter and
only adds xmlns declarations when the child's namespace differs from the
parent's namespace. This ensures proper namespace inheritance in XML output.

Changes:
- Add parent_namespace parameter to fields_to_elements()
- Only add nsmap when parent_namespace is None or differs from self.namespace
- Implement data_root wrapper in model_dump_xml_string()
- Use __class__.model_fields to avoid Pydantic deprecation warnings
- Remove unused imports (ruff lint fixes)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
elements: list[etree._Element] = []

# Get field info from model class (not instance to avoid deprecation warning)
for field_name, field_info in self.__class__.model_fields.items():
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accessing model_fields is deprecated and also does not support excluding defaults or None. And not really a reason to access it over __class__

continue

# Get the XML element name from alias or field name
xml_name = field_info.alias if field_info.alias else field_name
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why the AI thinks it is a good idea to extract the namespace from the field alias rather than using the classvar. There is a reason the class vars exist, and the alias could be wrong. I don't see it as clean software engineering to magically extract information from a field that is not designed for it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay I see why it does it. In case the var name would be different to the field name. this could happen if the field name has a dash in it's name 🤔

xml_name = field_info.alias if field_info.alias else field_name
# Strip prefix (e.g., "configuration:devicename" -> "devicename")
if ":" in xml_name:
xml_name = xml_name.split(":", 1)[1]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not a fan of this at all

container_name=xml_name,
parent_namespace=self.namespace,
)
elements.extend(child_elements)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Funny that it uses now the classvar

root = etree.Element(container_name)
for elem in elements:
root.append(elem)
return [root]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it works but could be done without nesting if's and for loop. The extra loop is not sexy


xml_name = field_info.alias if field_info.alias else field_name
if ":" in xml_name:
xml_name = xml_name.split(":", 1)[1]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplicated (ugly) code

if ":" in xml_name:
xml_name = xml_name.split(":", 1)[1]

if isinstance(value, XMLPydantifyModel):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not really needed as the check is done in fields_to_elements


# Fallback: return self as root
elements = self.fields_to_elements(container_name=self.prefix)
return elements[0] if elements else etree.Element(self.prefix)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what the idea of the use of self.prefix is, but this does not make sense at all for me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants