Repository: tefra/xsdata

Issue:
Unknown property error, when parsing inherited dataclasses with different namespaces
I try to use XSDATA to parse Business Process Model and Notation (BPMN) 2.0.2 diagrams.

https://www.omg.org/spec/BPMN/2.0.2/

The XS schema contains a waypoint Element that is not detected during parsing due to a namespace conflict caused by the parser class XmlMetaBuilder.

A working example with full error description and details (schema, generated dataclasses, parser code) can be found here:

https://github.com/THM-MA/XSDATA-waypoint

After debugging for several hours I truly believe that this is a parser bug.

Any help is highly appreciated!

Thank you so much for your support and this wonderful piece of software
Thomas


Discussion:
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; ![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=655&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=655&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=655&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=655) No Coverage information  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=655&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=655&metric=new_duplicated_lines_density&view=list)


codecov[bot]: # [Codecov](https://codecov.io/gh/tefra/xsdata/pull/655?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) Report
> Merging [#655](https://codecov.io/gh/tefra/xsdata/pull/655?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) (891e10b) into [master](https://codecov.io/gh/tefra/xsdata/commit/16b12b6135cdfdefbfcae1b8c76ba4602560117e?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) (16b12b6) will **decrease** coverage by `0.19%`.
> The diff coverage is `0.00%`.

[![Impacted file tree graph](https://codecov.io/gh/tefra/xsdata/pull/655/graphs/tree.svg?width=650&height=150&src=pr&token=YzDDLtywvl&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou)](https://codecov.io/gh/tefra/xsdata/pull/655?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou)

```diff
@@             Coverage Diff             @@
##            master     #655      +/-   ##
===========================================
- Coverage   100.00%   99.80%   -0.20%     
===========================================
  Files           94       95       +1     
  Lines         8214     8230      +16     
  Branches      1583     1579       -4     
===========================================
  Hits          8214     8214              
- Misses           0       16      +16     
```


| [Impacted Files](https://codecov.io/gh/tefra/xsdata/pull/655?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) | Coverage Δ | |
|---|---|---|
| [xsdata/utils/debug.py](https://codecov.io/gh/tefra/xsdata/pull/655/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL3V0aWxzL2RlYnVnLnB5) | `0.00% <0.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tefra/xsdata/pull/655?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tefra/xsdata/pull/655?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou). Last update [16b12b6...891e10b](https://codecov.io/gh/tefra/xsdata/pull/655?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou).

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; ![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=655&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=655&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=655&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=655) No Coverage information  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=655&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=655&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; ![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=655&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=655&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=655&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=655) No Coverage information  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=655&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=655&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; ![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=655&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=655&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=655&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=655&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=655) No Coverage information  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=655&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=655&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/setup.cfg b/setup.cfg
index 2694224c..cbaa8d30 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -83,3 +83,4 @@ addopts = --color=yes --benchmark-skip
 [coverage:run]
 omit =
     xsdata/__main__.py
+    xsdata/utils/debug.py
diff --git a/xsdata/formats/dataclass/models/builders.py b/xsdata/formats/dataclass/models/builders.py
index 5dbad68a..040f97be 100644
--- a/xsdata/formats/dataclass/models/builders.py
+++ b/xsdata/formats/dataclass/models/builders.py
@@ -42,7 +42,7 @@ class XmlMetaBuilder:
         self.element_name_generator = element_name_generator
         self.attribute_name_generator = attribute_name_generator
 
-    def build(self, clazz: Type, parent_ns: Optional[str]) -> XmlMeta:
+    def build(self, clazz: Type, parent_namespace: Optional[str]) -> XmlMeta:
         """Build the binding metadata for a dataclass and its fields."""
 
         self.class_type.verify_model(clazz)
@@ -59,7 +59,7 @@ class XmlMetaBuilder:
         local_name = getattr(meta, "name", None)
         local_name = local_name or element_name_generator(clazz.__name__)
         nillable = getattr(meta, "nillable", False)
-        namespace = getattr(meta, "namespace", parent_ns)
+        namespace = getattr(meta, "namespace", parent_namespace)
         module = sys.modules[clazz.__module__]
         qname = build_qname(namespace, local_name)
 
@@ -113,7 +113,7 @@ class XmlMetaBuilder:
     def build_vars(
         self,
         clazz: Type,
-        parent_ns: Optional[str],
+        namespace: Optional[str],
         element_name_generator: Callable,
         attribute_name_generator: Callable,
     ):
@@ -121,33 +121,40 @@ class XmlMetaBuilder:
         type_hints = get_type_hints(clazz)
         builder = XmlVarBuilder(
             class_type=self.class_type,
-            parent_ns=parent_ns,
             default_xml_type=self.default_xml_type(clazz),
             element_name_generator=element_name_generator,
             attribute_name_generator=attribute_name_generator,
         )
 
         for index, field in enumerate(self.class_type.get_fields(clazz)):
+
+            real_clazz = self.find_declared_class(clazz, field.name)
+            globalns = sys.modules[real_clazz.__module__].__dict__
+            parent_namespace = namespace
+            if real_clazz is not clazz and "Meta" in real_clazz.__dict__:
+                parent_namespace = getattr(real_clazz.Meta, "namespace", namespace)
+
             var = builder.build(
                 index,
                 field.name,
                 type_hints[field.name],
                 field.metadata,
                 field.init,
+                parent_namespace,
                 self.class_type.default_value(field),
-                self.find_globalns(clazz, field.name),
+                globalns,
             )
             if var is not None:
                 yield var
 
     @classmethod
-    def find_globalns(cls, clazz: Type, name: str) -> Optional[Dict]:
+    def find_declared_class(cls, clazz: Type, name: str) -> Type:
         for base in clazz.__mro__:
             ann = base.__dict__.get("__annotations__")
             if ann and name in ann:
-                return sys.modules[base.__module__].__dict__
+                return base
 
-        return None
+        raise XmlContextError(f"Failed to detect the declared class for field {name}")
 
     @classmethod
     def is_inner_class(cls, clazz: Type) -> bool:
@@ -203,7 +210,6 @@ class XmlMetaBuilder:
 class XmlVarBuilder:
 
     __slots__ = (
-        "parent_ns",
         "class_type",
         "default_xml_type",
         "element_name_generator",
@@ -213,12 +219,10 @@ class XmlVarBuilder:
     def __init__(
         self,
         class_type: ClassType,
-        parent_ns: Optional[str],
         default_xml_type: str,
         element_name_generator: Callable = return_input,
         attribute_name_generator: Callable = return_input,
     ):
-        self.parent_ns = parent_ns
         self.class_type = class_type
         self.default_xml_type = default_xml_type
         self.element_name_generator = element_name_generator
@@ -231,6 +235,7 @@ class XmlVarBuilder:
         type_hint: Any,
         metadata: Mapping[str, Any],
         init: bool,
+        parent_namespace: Optional[str],
         default_value: Any,
         globalns: Any,
         factory: Optional[Callable] = None,
@@ -268,13 +273,15 @@ class XmlVarBuilder:
 
         any_type = self.is_any_type(types, xml_type)
         clazz = first(tp for tp in types if self.class_type.is_model(tp))
-        namespaces = self.resolve_namespaces(xml_type, namespace)
+        namespaces = self.resolve_namespaces(xml_type, namespace, parent_namespace)
         default_namespace = self.default_namespace(namespaces)
         qname = build_qname(default_namespace, local_name)
 
         elements = {}
         wildcards = []
-        for choice in self.build_choices(name, choices, origin, globalns):
+        for choice in self.build_choices(
+            name, choices, origin, globalns, parent_namespace
+        ):
             if choice.is_element:
                 elements[choice.qname] = choice
             else:  # choice.is_wildcard:
@@ -304,7 +311,12 @@ class XmlVarBuilder:
         )
 
     def build_choices(
-        self, name: str, choices: List[Dict], factory: Callable, globalns: Any
+        self,
+        name: str,
+        choices: List[Dict],
+        factory: Callable,
+        globalns: Any,
+        parent_namespace: Optional[str],
     ) -> Iterator[XmlVar]:
         """Build the binding metadata for a compound dataclass field."""
         existing_types: Set[type] = set()
@@ -327,6 +339,7 @@ class XmlVarBuilder:
                 type_hint,
                 metadata,
                 True,
+                parent_namespace,
                 default_value,
                 globalns,
                 factory,
@@ -355,10 +368,12 @@ class XmlVarBuilder:
 
         return local_name
 
+    @classmethod
     def resolve_namespaces(
-        self,
+        cls,
         xml_type: Optional[str],
         namespace: Optional[str],
+        parent_namespace: Optional[str],
     ) -> Tuple[str, ...]:
         """
         Resolve the namespace(s) for the given xml type and the parent
@@ -375,7 +390,7 @@ class XmlVarBuilder:
         :param parent_namespace: The parent namespace
         """
         if xml_type in (XmlType.ELEMENT, XmlType.WILDCARD) and namespace is None:
-            namespace = self.parent_ns
+            namespace = parent_namespace
 
         if not namespace:
             return ()
@@ -383,11 +398,11 @@ class XmlVarBuilder:
         result = set()
         for ns in namespace.split():
             if ns == NamespaceType.TARGET_NS:
-                result.add(self.parent_ns or NamespaceType.ANY_NS)
+                result.add(parent_namespace or NamespaceType.ANY_NS)
             elif ns == NamespaceType.LOCAL_NS:
                 result.add("")
             elif ns == NamespaceType.OTHER_NS:
-                result.add(f"!{self.parent_ns or ''}")
+                result.add(f"!{parent_namespace or ''}")
             else:
                 result.add(ns)
 
diff --git a/xsdata/utils/debug.py b/xsdata/utils/debug.py
new file mode 100644
index 00000000..2e184ba1
--- /dev/null
+++ b/xsdata/utils/debug.py
@@ -0,0 +1,31 @@
+import json
+from pathlib import Path
+from typing import Any
+
+
+def dump(obj: Any):
+    """
+    Write any object into a dump json file.
+
+    For internal troubleshooting purposes only!!!
+    """
+
+    with Path.cwd().joinpath("xsdata_dump.json").open("w+") as f:
+        json.dump(convert(obj), f, indent=4)
+
+
+def convert(obj: Any) -> Any:
+    """Dump any obj into a readable dictionary."""
+    if not obj:
+        return obj
+
+    if isinstance(obj, list):
+        return list(map(convert, obj))
+
+    if isinstance(obj, dict):
+        return {key: convert(value) for key, value in obj.items()}
+
+    if hasattr(obj, "__slots__") and obj.__slots__:
+        return {name: convert(getattr(obj, name)) for name in obj.__slots__}
+
+    return str(obj)


Test patch:
diff --git a/tests/fixtures/models.py b/tests/fixtures/models.py
index f57fc44f..d686dc42 100644
--- a/tests/fixtures/models.py
+++ b/tests/fixtures/models.py
@@ -173,3 +173,20 @@ class Parent:
     @dataclass
     class Inner:
         pass
+
+
+@dataclass
+class TypeNS2:
+
+    class Meta:
+        namespace = "ns2"
+
+    x1: int = field(metadata=dict(type="Element"))
+
+@dataclass
+class TypeNS1(TypeNS2):
+
+    class Meta:
+        namespace = "ns1"
+
+    x2: int = field(metadata=dict(type="Element"))
\ No newline at end of file
diff --git a/tests/formats/dataclass/models/test_builders.py b/tests/formats/dataclass/models/test_builders.py
index 1a569cc7..b6395cb7 100644
--- a/tests/formats/dataclass/models/test_builders.py
+++ b/tests/formats/dataclass/models/test_builders.py
@@ -19,6 +19,7 @@ from tests.fixtures.models import ChoiceType
 from tests.fixtures.models import Parent
 from tests.fixtures.models import TypeA
 from tests.fixtures.models import TypeB
+from tests.fixtures.models import TypeNS1
 from tests.fixtures.models import UnionType
 from tests.fixtures.series import Country
 from tests.fixtures.submodels import ChoiceTypeChild
@@ -112,7 +113,13 @@ class XmlMetaBuilderTests(FactoryTestCase):
         actual = self.builder.build(ChoiceTypeChild, None)
         self.assertEqual(1, len(actual.choices))
         self.assertEqual(9, len(actual.choices[0].elements))
-        self.assertIsNone(self.builder.find_globalns(object, "foo"))
+
+        with self.assertRaises(XmlContextError):
+            self.builder.find_declared_class(object, "foo")
+
+    def test_build_locates_parent_namespace_per_field(self):
+        actual = self.builder.build(TypeNS1, None)
+        self.assertEqual(["{ns2}x1", "{ns1}x2"], list(actual.elements.keys()))
 
     def test_build_inner_type_has_no_target_qname(self):
         actual = self.builder.build(Parent.Inner, None)
@@ -248,7 +255,6 @@ class XmlVarBuilderTests(TestCase):
     def setUp(self) -> None:
         self.builder = XmlVarBuilder(
             class_type=class_types.get_type("dataclasses"),
-            parent_ns=None,
             default_xml_type=XmlType.ELEMENT,
             element_name_generator=return_input,
             attribute_name_generator=return_input,
@@ -261,7 +267,6 @@ class XmlVarBuilderTests(TestCase):
         globalns = sys.modules[ChoiceType.__module__].__dict__
         type_hints = get_type_hints(ChoiceType)
         class_field = fields(ChoiceType)[0]
-        self.builder.parent_ns = "bar"
 
         self.maxDiff = None
         actual = self.builder.build(
@@ -270,6 +275,7 @@ class XmlVarBuilderTests(TestCase):
             type_hints["choice"],
             class_field.metadata,
             True,
+            "bar",
             list,
             globalns,
         )
@@ -384,7 +390,7 @@ class XmlVarBuilderTests(TestCase):
     def test_build_validates_result(self):
         with self.assertRaises(XmlContextError) as cm:
             self.builder.build(
-                1, "foo", List[int], {"type": "Attributes"}, True, None, None
+                1, "foo", List[int], {"type": "Attributes"}, True, None, None, None
             )
 
         self.assertEqual(
@@ -394,35 +400,45 @@ class XmlVarBuilderTests(TestCase):
 
     def test_resolve_namespaces(self):
         func = self.builder.resolve_namespaces
-        self.builder.parent_ns = "bar"
 
-        self.assertEqual(("foo",), func(XmlType.ELEMENT, "foo"))
-        self.assertEqual((), func(XmlType.ELEMENT, ""))
-        self.assertEqual(("bar",), func(XmlType.ELEMENT, None))
+        actual = func(XmlType.ELEMENT, "foo", "bar")
+        self.assertEqual(("foo",), actual)
 
-        self.assertEqual((), func(XmlType.ATTRIBUTE, None))
+        actual = func(XmlType.ELEMENT, "", "bar")
+        self.assertEqual((), actual)
 
-        self.assertEqual(("bar",), func(XmlType.WILDCARD, None))
-        self.assertEqual(("##any",), func(XmlType.WILDCARD, "##any"))
+        actual = func(XmlType.ELEMENT, None, "bar")
+        self.assertEqual(("bar",), actual)
 
-        self.builder.parent_ns = ""
-        self.assertEqual(("##any",), func(XmlType.WILDCARD, "##targetNamespace"))
+        actual = func(XmlType.ATTRIBUTE, None, "bar")
+        self.assertEqual((), actual)
 
-        self.builder.parent_ns = None
-        self.assertEqual(("##any",), func(XmlType.WILDCARD, "##targetNamespace"))
+        actual = func(XmlType.WILDCARD, None, "bar")
+        self.assertEqual(("bar",), actual)
 
-        self.builder.parent_ns = "p"
-        self.assertEqual(("p",), func(XmlType.WILDCARD, "##targetNamespace"))
-        self.assertEqual(("",), func(XmlType.WILDCARD, "##local"))
-        self.assertEqual(("!p",), func(XmlType.WILDCARD, "##other"))
-        self.assertEqual(
-            ("", "!p"), tuple(sorted(func(XmlType.WILDCARD, "##other   ##local")))
-        )
+        actual = func(XmlType.WILDCARD, "##any", "bar")
+        self.assertEqual(("##any",), actual)
 
-        self.assertEqual(
-            ("foo", "p"),
-            tuple(sorted(func(XmlType.WILDCARD, "##targetNamespace   foo"))),
-        )
+        actual = func(XmlType.WILDCARD, "##targetNamespace", "")
+        self.assertEqual(("##any",), actual)
+
+        actual = func(XmlType.WILDCARD, "##targetNamespace", None)
+        self.assertEqual(("##any",), actual)
+
+        actual = func(XmlType.WILDCARD, "##targetNamespace", "p")
+        self.assertEqual(("p",), actual)
+
+        actual = func(XmlType.WILDCARD, "##local", "p")
+        self.assertEqual(("",), actual)
+
+        actual = func(XmlType.WILDCARD, "##other", "p")
+        self.assertEqual(("!p",), actual)
+
+        actual = func(XmlType.WILDCARD, "##other   ##local", "p")
+        self.assertEqual(("", "!p"), tuple(sorted(actual)))
+
+        actual = func(XmlType.WILDCARD, "##targetNamespace   foo", "p")
+        self.assertEqual(("foo", "p"), tuple(sorted(actual)))
 
     def test_analyze_types(self):
         actual = self.builder.analyze_types(List[List[Union[str, int]]], None)