Repository: tefra/xsdata

Issue:
Compound fields should use DerivedElement wrapper only if necessary
Currently the parser will always wrap any object with the `DerivedElement` wrapper in order to maintain the association between element and type.

```xml
<xs:choice maxOccurs="10">
   <xs:element name="a" type="xs:string" />
   <xs:element name="b" type="xs:string" />
</xs:choice>
```

When all elements have unique types the parser should actually avoid using the wrapper as the type is the association. 



Discussion:
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo.png' alt='No Coverage information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308) No Coverage information  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308&metric=new_duplicated_lines_density&view=list)


codecov[bot]: # [Codecov](https://codecov.io/gh/tefra/xsdata/pull/308?src=pr&el=h1) Report
> Merging [#308](https://codecov.io/gh/tefra/xsdata/pull/308?src=pr&el=desc) into [master](https://codecov.io/gh/tefra/xsdata/commit/6afe163a1b83ec1db5129a6f14575e98e9391244?el=desc) will **decrease** coverage by `0.12%`.
> The diff coverage is `88.09%`.

[![Impacted file tree graph](https://codecov.io/gh/tefra/xsdata/pull/308/graphs/tree.svg?width=650&height=150&src=pr&token=YzDDLtywvl)](https://codecov.io/gh/tefra/xsdata/pull/308?src=pr&el=tree)

```diff
@@             Coverage Diff             @@
##            master     #308      +/-   ##
===========================================
- Coverage   100.00%   99.87%   -0.13%     
===========================================
  Files           69       69              
  Lines         5476     5510      +34     
  Branches       940      954      +14     
===========================================
+ Hits          5476     5503      +27     
- Misses           0        4       +4     
- Partials         0        3       +3     
```


| [Impacted Files](https://codecov.io/gh/tefra/xsdata/pull/308?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [xsdata/formats/dataclass/parsers/json.py](https://codecov.io/gh/tefra/xsdata/pull/308/diff?src=pr&el=tree#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL3BhcnNlcnMvanNvbi5weQ==) | `96.34% <66.66%> (-3.66%)` | :arrow_down: |
| [xsdata/formats/dataclass/models/elements.py](https://codecov.io/gh/tefra/xsdata/pull/308/diff?src=pr&el=tree#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL21vZGVscy9lbGVtZW50cy5weQ==) | `97.87% <90.47%> (-2.13%)` | :arrow_down: |
| [xsdata/formats/dataclass/context.py](https://codecov.io/gh/tefra/xsdata/pull/308/diff?src=pr&el=tree#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL2NvbnRleHQucHk=) | `100.00% <100.00%> (ø)` | |
| [xsdata/formats/dataclass/serializers/xml.py](https://codecov.io/gh/tefra/xsdata/pull/308/diff?src=pr&el=tree#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL3NlcmlhbGl6ZXJzL3htbC5weQ==) | `100.00% <100.00%> (ø)` | |
| [xsdata/codegen/sanitizer.py](https://codecov.io/gh/tefra/xsdata/pull/308/diff?src=pr&el=tree#diff-eHNkYXRhL2NvZGVnZW4vc2FuaXRpemVyLnB5) | `100.00% <0.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tefra/xsdata/pull/308?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tefra/xsdata/pull/308?src=pr&el=footer). Last update [6afe163...3b75410](https://codecov.io/gh/tefra/xsdata/pull/308?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo.png' alt='No Coverage information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308) No Coverage information  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo.png' alt='No Coverage information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308) No Coverage information  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo.png' alt='No Coverage information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308) No Coverage information  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo.png' alt='No Coverage information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308) No Coverage information  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=308&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo.png' alt='No Coverage information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308) No Coverage information  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=308&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/xsdata/formats/dataclass/context.py b/xsdata/formats/dataclass/context.py
index 826a706a..b730d1be 100644
--- a/xsdata/formats/dataclass/context.py
+++ b/xsdata/formats/dataclass/context.py
@@ -184,6 +184,7 @@ class XmlContext:
         parent_namespace: Optional[str],
         choices: List[Dict],
     ):
+        existing = set()
         globalns = sys.modules[clazz.__module__].__dict__
         for choice in choices:
             xml_type = XmlType.WILDCARD if choice.get("wildcard") else XmlType.ELEMENT
@@ -192,20 +193,23 @@ class XmlContext:
             default_namespace = self.default_namespace(namespaces)
 
             types = self.real_types(_eval_type(choice["type"], globalns, None))
+            derived = any(True for tp in types if tp in existing)
             is_class = any(is_dataclass(clazz) for clazz in types)
             xml_clazz = XmlType.to_xml_class(xml_type)
             qname = build_qname(default_namespace, choice.get("name", "any"))
+            nillable = choice.get("nillable", False)
 
             yield xml_clazz(
                 name=parent_name,
                 qname=qname,
                 namespaces=namespaces,
-                nillable=choice.get("nillable", False),
+                nillable=nillable,
                 dataclass=is_class,
                 tokens=choice.get("tokens", False),
-                derived=True,
+                derived=derived or nillable,
                 types=types,
             )
+            existing.update(types)
 
     @classmethod
     def resolve_namespaces(
diff --git a/xsdata/formats/dataclass/models/elements.py b/xsdata/formats/dataclass/models/elements.py
index f8f5363e..01000f12 100644
--- a/xsdata/formats/dataclass/models/elements.py
+++ b/xsdata/formats/dataclass/models/elements.py
@@ -1,5 +1,6 @@
 from dataclasses import dataclass
 from dataclasses import field
+from dataclasses import is_dataclass
 from enum import auto
 from enum import IntEnum
 from typing import Any
@@ -119,8 +120,8 @@ class XmlVar:
         """Match and return a choice field by its qualified name."""
         return None
 
-    def find_choice_typed(self, tp: Type) -> Optional["XmlVar"]:
-        """Match and return a choice field by its types."""
+    def find_value_choice(self, value: Any) -> Optional["XmlVar"]:
+        """Match and return a choice field that matches the given value."""
         return None
 
 
@@ -158,10 +159,31 @@ class XmlElements(XmlVar):
 
         return None
 
-    def find_choice_typed(self, tp: Type) -> Optional["XmlVar"]:
-        """Match and return a choice field by its types."""
+    def find_value_choice(self, value: Any) -> Optional["XmlVar"]:
+        """Match and return a choice field that matches the given value."""
+
+        if isinstance(value, list):
+            tp = None if not value else type(value[0])
+            tokens = True
+            is_class = False
+        else:
+            tp = type(value)
+            tokens = False
+            is_class = is_dataclass(value)
+
+        def match_type(match: Type, types: List[Type]) -> bool:
+            for candidate in types:
+                if tp == candidate or (is_class and issubclass(match, candidate)):
+                    return True
+
+            return False
+
         for choice in self.choices:
-            if tp in choice.types:
+
+            if choice.is_any_type or tokens != choice.tokens:
+                continue
+
+            if (not tp and choice.nillable) or (tp and match_type(tp, choice.types)):
                 return choice
 
         return None
diff --git a/xsdata/formats/dataclass/parsers/json.py b/xsdata/formats/dataclass/parsers/json.py
index 696f4648..72e1ded3 100644
--- a/xsdata/formats/dataclass/parsers/json.py
+++ b/xsdata/formats/dataclass/parsers/json.py
@@ -3,6 +3,7 @@ import json
 import pathlib
 from dataclasses import dataclass
 from dataclasses import field
+from dataclasses import fields
 from typing import Any
 from typing import Dict
 from typing import Type
@@ -88,7 +89,10 @@ class JsonParser(AbstractParser):
         )
 
     def bind_choice(self, value: Any, var: XmlVar) -> Any:
-        if isinstance(value, dict) and "qname" in value:
+        if not isinstance(value, dict):
+            return value
+
+        if "qname" in value:
             qname = value["qname"]
             choice = var.find_choice(qname)
 
@@ -102,7 +106,16 @@ class JsonParser(AbstractParser):
 
             return self.parse_context(value, AnyElement)
 
-        return value
+        keys = set(value.keys())
+        for choice in var.choices:
+            if not choice.dataclass:
+                continue
+
+            attrs = {f.name for f in fields(choice.clazz)}
+            if attrs == keys:
+                return self.bind_value(choice, value)
+
+        raise ParserError(f"XmlElements undefined choice: `{var.name}` for `{value}`")
 
     @staticmethod
     def get_value(data: Dict, var: XmlVar) -> Any:
diff --git a/xsdata/formats/dataclass/serializers/xml.py b/xsdata/formats/dataclass/serializers/xml.py
index d67153d0..79f7b4b0 100644
--- a/xsdata/formats/dataclass/serializers/xml.py
+++ b/xsdata/formats/dataclass/serializers/xml.py
@@ -135,14 +135,14 @@ class XmlSerializer(AbstractSerializer):
             yield from self.write_data(value, var, namespace)
         elif var.tokens:
             yield from self.write_tokens(value, var, namespace)
+        elif var.is_elements:
+            yield from self.write_elements(value, var, namespace)
         elif var.is_list and isinstance(value, list):
             yield from self.write_list(value, var, namespace)
         elif var.is_any_type:
             yield from self.write_any_type(value, var, namespace)
         elif var.dataclass:
             yield from self.write_xsi_type(value, var, namespace)
-        elif var.is_elements:
-            yield from self.write_choice(value, var, namespace)
         elif var.is_element:
             yield from self.write_element(value, var, namespace)
         else:
@@ -227,6 +227,14 @@ class XmlSerializer(AbstractSerializer):
             f"{value.__class__.__name__} is not derived from {clazz.__name__}"
         )
 
+    def write_elements(self, value: Any, var: XmlVar, namespace: NoneStr) -> Generator:
+        """Produce an events stream from compound elements field."""
+        if isinstance(value, list):
+            for choice in value:
+                yield from self.write_choice(choice, var, namespace)
+        else:
+            yield from self.write_choice(value, var, namespace)
+
     def write_choice(self, value: Any, var: XmlVar, namespace: NoneStr) -> Generator:
         """
         Produce an events stream for the given value of a compound elements
@@ -243,8 +251,7 @@ class XmlSerializer(AbstractSerializer):
             choice = var.find_choice(value.qname)
             func = self.write_any_type
         else:
-            tp = type(value)
-            choice = var.find_choice_typed(tp)
+            choice = var.find_value_choice(value)
             func = self.write_value
 
         if not choice:


Test patch:
diff --git a/tests/fixtures/defxmlschema/chapter12.json b/tests/fixtures/defxmlschema/chapter12.json
index f074392a..0b977f9b 100644
--- a/tests/fixtures/defxmlschema/chapter12.json
+++ b/tests/fixtures/defxmlschema/chapter12.json
@@ -1,60 +1,48 @@
 {
     "shirt_or_hat_or_umbrella": [
         {
-            "qname": "shirt",
-            "value": {
-                "number": 557,
-                "name": "Short-Sleeved Linen Blouse",
-                "size_or_color_or_description": [
-                    {
-                        "qname": "size",
-                        "value": {
-                            "value": 10,
-                            "system": "US-DRESS"
+            "number": 557,
+            "name": "Short-Sleeved Linen Blouse",
+            "size_or_color_or_description": [
+                {
+                    "value": 10,
+                    "system": "US-DRESS"
+                },
+                {
+                    "value": "blue"
+                },
+                {
+                    "w3_org_1999_xhtml_element": [
+                        "\n      This shirt is the ",
+                        {
+                            "qname": "{http://www.w3.org/1999/xhtml}b",
+                            "text": "best-selling",
+                            "tail": " shirt in\n      our catalog! ",
+                            "ns_map": {
+                                "xsi": "http://www.w3.org/2001/XMLSchema-instance",
+                                "xhtml": "http://www.w3.org/1999/xhtml",
+                                "oth": "http://example.org/oth"
+                            },
+                            "children": [],
+                            "attributes": {}
+                        },
+                        {
+                            "qname": "{http://www.w3.org/1999/xhtml}br",
+                            "text": null,
+                            "tail": " Note: runs large.\n    ",
+                            "ns_map": {
+                                "xsi": "http://www.w3.org/2001/XMLSchema-instance",
+                                "xhtml": "http://www.w3.org/1999/xhtml",
+                                "oth": "http://example.org/oth"
+                            },
+                            "children": [],
+                            "attributes": {}
                         }
-                    },
-                    {
-                        "qname": "color",
-                        "value": {
-                            "value": "blue"
-                        }
-                    },
-                    {
-                        "qname": "description",
-                        "value": {
-                            "w3_org_1999_xhtml_element": [
-                                "\n      This shirt is the ",
-                                {
-                                    "qname": "{http://www.w3.org/1999/xhtml}b",
-                                    "text": "best-selling",
-                                    "tail": " shirt in\n      our catalog! ",
-                                    "ns_map": {
-                                        "xsi": "http://www.w3.org/2001/XMLSchema-instance",
-                                        "xhtml": "http://www.w3.org/1999/xhtml",
-                                        "oth": "http://example.org/oth"
-                                    },
-                                    "children": [],
-                                    "attributes": {}
-                                },
-                                {
-                                    "qname": "{http://www.w3.org/1999/xhtml}br",
-                                    "text": null,
-                                    "tail": " Note: runs large.\n    ",
-                                    "ns_map": {
-                                        "xsi": "http://www.w3.org/2001/XMLSchema-instance",
-                                        "xhtml": "http://www.w3.org/1999/xhtml",
-                                        "oth": "http://example.org/oth"
-                                    },
-                                    "children": [],
-                                    "attributes": {}
-                                }
-                            ]
-                        }
-                    }
-                ],
-                "eff_date": "2002-04-02",
-                "other_attributes": {}
-            }
+                    ]
+                }
+            ],
+            "eff_date": "2002-04-02",
+            "other_attributes": {}
         },
         {
             "qname": "hat",
@@ -63,10 +51,7 @@
                 "name": "Ten-Gallon Hat",
                 "size_or_color_or_description": [
                     {
-                        "qname": "color",
-                        "value": {
-                            "value": "red"
-                        }
+                        "value": "red"
                     }
                 ],
                 "eff_date": "1900-01-01",
diff --git a/tests/fixtures/defxmlschema/chapter17.json b/tests/fixtures/defxmlschema/chapter17.json
index 02275ddb..857e1244 100644
--- a/tests/fixtures/defxmlschema/chapter17.json
+++ b/tests/fixtures/defxmlschema/chapter17.json
@@ -3,24 +3,18 @@
     "items": {
         "shirt_or_hat": [
             {
-                "qname": "shirt",
-                "value": {
-                    "quantity": 1,
-                    "color": {
-                        "value": "blue"
-                    },
-                    "number": 557
-                }
+                "quantity": 1,
+                "color": {
+                    "value": "blue"
+                },
+                "number": 557
             },
             {
-                "qname": "shirt",
-                "value": {
-                    "quantity": 1,
-                    "color": {
-                        "value": "sage"
-                    },
-                    "number": 557
-                }
+                "quantity": 1,
+                "color": {
+                    "value": "sage"
+                },
+                "number": 557
             },
             {
                 "qname": "hat",
diff --git a/tests/formats/dataclass/parsers/test_json.py b/tests/formats/dataclass/parsers/test_json.py
index fd2b0b21..50e95d5a 100644
--- a/tests/formats/dataclass/parsers/test_json.py
+++ b/tests/formats/dataclass/parsers/test_json.py
@@ -1,3 +1,4 @@
+from dataclasses import make_dataclass
 from unittest.case import TestCase
 
 from tests import fixtures_dir
@@ -56,11 +57,37 @@ class JsonParserTests(TestCase):
         self.assertEqual(["foo"], JsonParser.get_value(data, bar_field))
         self.assertIsNone(JsonParser.get_value({}, bar_field))
 
-    def test_bind_choice_with_raw_value(self):
+    def test_bind_choice_with_raw_primitive_value(self):
         var = XmlElements(qname="compound", name="compound")
         parser = JsonParser()
         self.assertEqual(1, parser.bind_choice(1, var))
 
+    def test_bind_choice_with_raw_dict(self):
+        a = make_dataclass("a", [("x", int)])
+        b = make_dataclass("b", [("x", int), ("y", str)])
+
+        var = XmlElements(
+            qname="compound",
+            name="compound",
+            choices=[
+                XmlElement(qname="c", name="c", types=[int]),
+                XmlElement(qname="a", name="a", types=[a], dataclass=True),
+                XmlElement(qname="b", name="b", types=[b], dataclass=True),
+            ],
+        )
+
+        parser = JsonParser()
+        self.assertEqual(a(1), parser.bind_choice({"x": 1}, var))
+        self.assertEqual(b(1, "2"), parser.bind_choice({"x": 1, "y": "2"}, var))
+
+        with self.assertRaises(ParserError) as cm:
+            parser.bind_choice({"x": 1, "y": "2", "z": 3}, var)
+
+        self.assertEqual(
+            "XmlElements undefined choice: `compound` for `{'x': 1, 'y': '2', 'z': 3}`",
+            str(cm.exception),
+        )
+
     def test_bind_choice_with_derived_value(self):
         var = XmlElements(
             qname="compound",
diff --git a/tests/formats/dataclass/serializers/test_xml.py b/tests/formats/dataclass/serializers/test_xml.py
index 631c6f26..c36898fc 100644
--- a/tests/formats/dataclass/serializers/test_xml.py
+++ b/tests/formats/dataclass/serializers/test_xml.py
@@ -335,15 +335,21 @@ class XmlSerializerTests(TestCase):
         var = XmlElements(
             name="compound",
             qname="compound",
-            choices=[XmlElement(qname="a", name="a", types=[int])],
+            choices=[
+                XmlElement(qname="a", name="a", types=[int]),
+                XmlElement(qname="b", name="b", types=[int], tokens=True),
+            ],
         )
         expected = [
             (XmlWriterEvent.START, "a"),
             (XmlWriterEvent.DATA, 1),
             (XmlWriterEvent.END, "a"),
+            (XmlWriterEvent.START, "b"),
+            (XmlWriterEvent.DATA, [1, 2]),
+            (XmlWriterEvent.END, "b"),
         ]
 
-        result = self.serializer.write_value(1, var, "xsdata")
+        result = self.serializer.write_value([1, [1, 2]], var, "xsdata")
         self.assertIsInstance(result, Generator)
         self.assertEqual(expected, list(result))
 
diff --git a/tests/formats/dataclass/test_context.py b/tests/formats/dataclass/test_context.py
index e0bec981..166149b0 100644
--- a/tests/formats/dataclass/test_context.py
+++ b/tests/formats/dataclass/test_context.py
@@ -305,7 +305,7 @@ class XmlContextTests(TestCase):
                     dataclass=True,
                     types=[Node],
                     namespaces=["foo"],
-                    derived=True,
+                    derived=False,
                 ),
                 XmlElement(
                     name="compound",
@@ -313,7 +313,7 @@ class XmlContextTests(TestCase):
                     tokens=True,
                     types=[str],
                     namespaces=["bar"],
-                    derived=True,
+                    derived=False,
                 ),
                 XmlElement(
                     name="compound",
@@ -323,12 +323,20 @@ class XmlContextTests(TestCase):
                     namespaces=["bar"],
                     derived=True,
                 ),
+                XmlElement(
+                    name="compound",
+                    qname="{bar}z",
+                    nillable=False,
+                    types=[int],
+                    namespaces=["bar"],
+                    derived=True,
+                ),
                 XmlWildcard(
                     name="compound",
                     qname="{http://www.w3.org/1999/xhtml}any",
                     types=[object],
                     namespaces=["http://www.w3.org/1999/xhtml"],
-                    derived=True,
+                    derived=False,
                 ),
             ],
             types=[object],
@@ -472,6 +480,7 @@ class Node:
                 {"name": "node", "type": Type["Node"], "namespace": "foo"},
                 {"name": "x", "type": List[str], "tokens": True},
                 {"name": "y", "type": List[int], "nillable": True},
+                {"name": "z", "type": List[int]},
                 {
                     "wildcard": True,
                     "type": object,
diff --git a/tests/formats/dataclass/test_elements.py b/tests/formats/dataclass/test_elements.py
index 7a4cc9ed..42ce2aaf 100644
--- a/tests/formats/dataclass/test_elements.py
+++ b/tests/formats/dataclass/test_elements.py
@@ -1,4 +1,5 @@
 from dataclasses import dataclass
+from dataclasses import make_dataclass
 from dataclasses import replace
 from unittest.case import TestCase
 
@@ -62,9 +63,9 @@ class XmlValTests(TestCase):
         var = XmlVar(name="foo", qname="foo")
         self.assertIsNone(var.find_choice("foo"))
 
-    def test_find_choice_typed(self):
+    def test_find_value_choice(self):
         var = XmlVar(name="foo", qname="foo")
-        self.assertIsNone(var.find_choice_typed(int))
+        self.assertIsNone(var.find_value_choice(int))
 
 
 class XmlElementTests(TestCase):
@@ -104,6 +105,26 @@ class XmlElementsTests(TestCase):
         self.assertEqual(choices[1], var.find_choice("b"))
         self.assertTrue(var.matches("b"))
 
+    def test_find_value_choice(self):
+        c = make_dataclass("C", fields=[])
+        d = make_dataclass("D", fields=[], bases=(c,))
+
+        var = XmlElements(
+            name="compound",
+            qname="compound",
+            choices=[
+                XmlElement(qname="a", name="a", types=[int]),
+                XmlElement(qname="b", name="b", types=[int], tokens=True),
+                XmlElement(qname="c", name="c", types=[c], dataclass=True),
+            ],
+        )
+
+        self.assertIsNone(var.find_value_choice("foo"))
+        self.assertEqual(var.choices[0], var.find_value_choice(1))
+        self.assertEqual(var.choices[1], var.find_value_choice([1, 2]))
+        self.assertEqual(var.choices[2], var.find_value_choice(d()))
+        self.assertEqual(var.choices[2], var.find_value_choice(c()))
+
 
 class XmlWildcardTests(TestCase):
     def test_property_is_mixed_content(self):