Repository: tefra/xsdata
Pull request #495 (2021-05-19 21:14:10)

Refactor JsonParser
###Todos

 - [x] Support failing on unknown properties
 - [x] Strict binding process (match types or exit)
 - [x] Enhance DerivedElement support
 - [x] Support required properties (fields without default values)
 - [x] Decrease w3c test suite roundtrip failures
 - [x] Use new typing utils for clazz validation
 - [x] Inject parser config

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=495&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=495&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&resolved=false&types=VULNERABILITY)  
[<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT)  
[<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=495&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=495&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495) 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=495&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=495&metric=new_duplicated_lines_density&view=list)


codecov[bot]: # [Codecov](https://codecov.io/gh/tefra/xsdata/pull/495?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) Report
> Merging [#495](https://codecov.io/gh/tefra/xsdata/pull/495?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) (01d5896) into [master](https://codecov.io/gh/tefra/xsdata/commit/a24f0d45bbcb39647294ec2cce7d7c0d370b1547?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) (a24f0d4) will **decrease** coverage by `0.09%`.
> The diff coverage is `94.65%`.

[![Impacted file tree graph](https://codecov.io/gh/tefra/xsdata/pull/495/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/495?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     #495      +/-   ##
===========================================
- Coverage   100.00%   99.90%   -0.10%     
===========================================
  Files           84       84              
  Lines         7386     7424      +38     
  Branches      1328     1335       +7     
===========================================
+ Hits          7386     7417      +31     
- Misses           0        4       +4     
- Partials         0        3       +3     
```


| [Impacted Files](https://codecov.io/gh/tefra/xsdata/pull/495?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) | Coverage Δ | |
|---|---|---|
| [xsdata/formats/dataclass/context.py](https://codecov.io/gh/tefra/xsdata/pull/495/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL2NvbnRleHQucHk=) | `96.51% <25.00%> (-3.49%)` | :arrow_down: |
| [xsdata/formats/dataclass/parsers/json.py](https://codecov.io/gh/tefra/xsdata/pull/495/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL3BhcnNlcnMvanNvbi5weQ==) | `97.94% <96.49%> (-2.06%)` | :arrow_down: |
| [xsdata/formats/dataclass/models/elements.py](https://codecov.io/gh/tefra/xsdata/pull/495/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL21vZGVscy9lbGVtZW50cy5weQ==) | `100.00% <100.00%> (ø)` | |
| [xsdata/formats/dataclass/models/generics.py](https://codecov.io/gh/tefra/xsdata/pull/495/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL21vZGVscy9nZW5lcmljcy5weQ==) | `100.00% <100.00%> (ø)` | |
| [xsdata/formats/dataclass/parsers/nodes.py](https://codecov.io/gh/tefra/xsdata/pull/495/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL3BhcnNlcnMvbm9kZXMucHk=) | `100.00% <100.00%> (ø)` | |
| [xsdata/formats/dataclass/serializers/xml.py](https://codecov.io/gh/tefra/xsdata/pull/495/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL3NlcmlhbGl6ZXJzL3htbC5weQ==) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tefra/xsdata/pull/495?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/495?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou). Last update [a24f0d4...01d5896](https://codecov.io/gh/tefra/xsdata/pull/495?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!

[<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=495&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=495&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&resolved=false&types=VULNERABILITY)  
[<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT)  
[<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=495&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=495&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495) 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=495&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&resolved=false&types=VULNERABILITY)  
[<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT)  
[<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=495&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=495&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495) 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=495&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&resolved=false&types=VULNERABILITY)  
[<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT)  
[<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=495&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=495&resolved=false&types=CODE_SMELL) [1 Code Smell](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495) 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=495&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&resolved=false&types=VULNERABILITY)  
[<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT)  
[<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=495&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=495&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495) 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=495&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&resolved=false&types=VULNERABILITY)  
[<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT)  
[<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=495&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=495&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495) 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=495&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&resolved=false&types=VULNERABILITY)  
[<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT)  
[<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=495&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=495&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495) 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=495&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&resolved=false&types=VULNERABILITY)  
[<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT)  
[<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=495&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=495&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495) 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=495&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&resolved=false&types=VULNERABILITY)  
[<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT)  
[<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=495&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=495&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495) 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=495&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495&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=495&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&resolved=false&types=VULNERABILITY)  
[<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [<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/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=495&resolved=false&types=SECURITY_HOTSPOT)  
[<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=495&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=495&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=495&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=495) 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=495&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=495&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 437dbfd4..c80b956a 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -2,7 +2,7 @@ exclude: tests/fixtures|docs/examples
 
 repos:
   - repo: https://github.com/asottile/pyupgrade
-    rev: v2.15.0
+    rev: v2.18.1
     hooks:
       - id: pyupgrade
         args: [--py37-plus]
@@ -11,7 +11,7 @@ repos:
     hooks:
       - id: reorder-python-imports
   - repo: https://github.com/ambv/black
-    rev: 21.5b0
+    rev: 21.5b1
     hooks:
       - id: black
   - repo: https://gitlab.com/pycqa/flake8
@@ -26,7 +26,7 @@ repos:
         args: ["--suppress-none-returning"]
 
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v3.4.0
+    rev: v4.0.1
     hooks:
       - id: trailing-whitespace
       - id: end-of-file-fixer
diff --git a/docs/json.rst b/docs/json.rst
index 50058f8b..3185c83e 100644
--- a/docs/json.rst
+++ b/docs/json.rst
@@ -2,8 +2,22 @@
 JSON Binding
 ============
 
-Binding JSON lacks a bit in features and for edge cases with wildcards and derived
-types doing roundtrip conversions is not always possible.
+All binding modules rely on a :class:`~xsdata.formats.dataclass.context.XmlContext`
+instance to cache marshalling information.
+
+It's recommended to either reuse the same parser/serializer instance or reuse the
+context instance.
+
+.. code-block::
+
+    from xsdata.formats.dataclass.context import XmlContext
+    from xsdata.formats.dataclass.parsers import JsonParser
+    from xsdata.formats.dataclass.serializers import JsonSerializer
+
+    context = XmlContext()
+    parser = JsonParser(context=context)
+    serializer = JsonSerializer(context=context)
+
 
 .. testsetup:: *
 
@@ -91,6 +105,28 @@ From json path
     BookForm(author='Hightower, Kim', title='The First Book', genre='Fiction', price=44.95, pub_date=XmlDate(2000, 10, 1), review='An amazing story of nothing.', id='bk001', lang='en')
 
 
+Ignore unknown properties
+-------------------------
+
+.. doctest::
+
+    >>> from tests.fixtures.books import *  # Import all classes
+    >>> from xsdata.formats.dataclass.parsers.config import ParserConfig
+    ...
+    >>> config = ParserConfig(
+    ...     fail_on_unknown_properties=False,
+    ... )
+    >>> json_string = """{
+    ...   "author": "Hightower, Kim",
+    ...   "unknown_property": "I will fail"
+    ... }"""
+    >>> parser = JsonParser(config=config)
+    >>> parser.from_string(json_string, BookForm)
+    BookForm(author='Hightower, Kim', title=None, genre=None, price=None, pub_date=None, review=None, id=None, lang='en')
+
+API :ref:`Reference <ParserConfig>`.
+
+
 Unknown json target type
 ------------------------
 
@@ -113,6 +149,12 @@ all the imported modules to find a matching dataclass.
     >>> parser.from_string(json_string)
     BookForm(author='Hightower, Kim', title='The First Book', genre='Fiction', price=44.95, pub_date=XmlDate(2000, 10, 1), review='An amazing story of nothing.', id='bk001', lang='en')
 
+.. warning::
+
+    The class locator searches for a dataclass that includes all the input object
+    properties. This process doesn't work for documents with unknown properties even
+    if the configuration option is disabled!
+
 
 List of Objects
 ---------------
diff --git a/xsdata/formats/converter.py b/xsdata/formats/converter.py
index 38d58062..3bd7baf0 100644
--- a/xsdata/formats/converter.py
+++ b/xsdata/formats/converter.py
@@ -99,7 +99,7 @@ class ConverterFactory:
             return None
 
         if isinstance(value, list):
-            return " ".join([self.serialize(val, **kwargs) for val in value])
+            return " ".join(self.serialize(val, **kwargs) for val in value)
 
         instance = self.value_converter(value)
         return instance.serialize(value, **kwargs)
@@ -232,7 +232,10 @@ class BoolConverter(Converter):
 
             raise ConverterError(f"Invalid bool literal '{value}'")
 
-        return True if value else False
+        if value is True or value is False:
+            return value
+
+        raise ConverterError(f"Invalid bool literal '{value}'")
 
     def serialize(self, value: bool, **kwargs: Any) -> str:
         return "true" if value else "false"
@@ -362,7 +365,7 @@ class QNameConverter(Converter):
         return f"{prefix}:{tag}" if prefix else tag
 
     @staticmethod
-    def resolve(value: str, ns_map: Optional[Dict]) -> Tuple:
+    def resolve(value: str, ns_map: Optional[Dict] = None) -> Tuple:
         value = value.strip()
 
         if not value:
diff --git a/xsdata/formats/dataclass/context.py b/xsdata/formats/dataclass/context.py
index d48b745f..a1ce27d8 100644
--- a/xsdata/formats/dataclass/context.py
+++ b/xsdata/formats/dataclass/context.py
@@ -2,7 +2,6 @@ import sys
 from collections import defaultdict
 from dataclasses import dataclass
 from dataclasses import field
-from dataclasses import fields
 from dataclasses import is_dataclass
 from typing import Any
 from typing import Callable
@@ -12,6 +11,7 @@ from typing import Optional
 from typing import Set
 from typing import Type
 
+from xsdata.exceptions import XmlContextError
 from xsdata.formats.bindings import T
 from xsdata.formats.dataclass.models.builders import XmlMetaBuilder
 from xsdata.formats.dataclass.models.elements import XmlMeta
@@ -105,7 +105,7 @@ class XmlContext:
         types: List[Type] = self.find_types(qname)
         return types[-1] if types else None
 
-    def find_type_by_fields(self, field_names: Set) -> Optional[Type[T]]:
+    def find_type_by_fields(self, field_names: Set[str]) -> Optional[Type[T]]:
         """
         Find a dataclass from all the imported modules that matches the given
         list of field names.
@@ -116,7 +116,7 @@ class XmlContext:
         self.build_xsi_cache()
         for types in self.xsi_cache.values():
             for clazz in types:
-                if not field_names.difference({attr.name for attr in fields(clazz)}):
+                if self.local_names_match(field_names, clazz):
                     return clazz
 
         return None
@@ -157,6 +157,14 @@ class XmlContext:
             )
         return self.cache[clazz]
 
+    def local_names_match(self, names: Set[str], clazz: Type) -> bool:
+        try:
+            meta = self.build(clazz)
+            local_names = {var.local_name for var in meta.get_all_vars()}
+            return not names.difference(local_names)
+        except XmlContextError:
+            return False
+
     @classmethod
     def is_derived(cls, obj: Any, clazz: Type) -> bool:
         """
diff --git a/xsdata/formats/dataclass/filters.py b/xsdata/formats/dataclass/filters.py
index f3e8e93b..5c0c3d8d 100644
--- a/xsdata/formats/dataclass/filters.py
+++ b/xsdata/formats/dataclass/filters.py
@@ -305,16 +305,14 @@ class Filters:
 
         next_indent = indent + 4
         value = "\n".join(
-            [
-                f'{" " * next_indent}"{line}"'
-                for line in textwrap.wrap(
-                    value,
-                    width=self.max_line_length - next_indent - 2,  # plus quotes
-                    drop_whitespace=False,
-                    replace_whitespace=False,
-                    break_long_words=True,
-                )
-            ]
+            f'{" " * next_indent}"{line}"'
+            for line in textwrap.wrap(
+                value,
+                width=self.max_line_length - next_indent - 2,  # plus quotes
+                drop_whitespace=False,
+                replace_whitespace=False,
+                break_long_words=True,
+            )
         )
         return f"(\n{value}\n{' ' * indent})"
 
diff --git a/xsdata/formats/dataclass/models/builders.py b/xsdata/formats/dataclass/models/builders.py
index abe8c982..5ca4acdb 100644
--- a/xsdata/formats/dataclass/models/builders.py
+++ b/xsdata/formats/dataclass/models/builders.py
@@ -44,7 +44,7 @@ class XmlMetaBuilder:
         """Build the binding metadata for a dataclass and its fields."""
 
         if not is_dataclass(clazz):
-            raise XmlContextError(f"Object {clazz} is not a dataclass.")
+            raise XmlContextError(f"Type '{clazz}' is not a dataclass.")
 
         # Fetch the dataclass meta settings and make sure we don't inherit
         # the parent class meta.
diff --git a/xsdata/formats/dataclass/models/elements.py b/xsdata/formats/dataclass/models/elements.py
index c4acf9f2..82afc66f 100644
--- a/xsdata/formats/dataclass/models/elements.py
+++ b/xsdata/formats/dataclass/models/elements.py
@@ -7,8 +7,10 @@ from typing import List
 from typing import Mapping
 from typing import Optional
 from typing import Sequence
+from typing import Set
 from typing import Type
 
+from xsdata.formats.converter import converter
 from xsdata.models.enums import NamespaceType
 from xsdata.utils import collections
 from xsdata.utils.constants import EMPTY_SEQUENCE
@@ -171,6 +173,10 @@ class XmlVar(MetaMixin):
         else:
             self.is_text = True
 
+    @property
+    def element_types(self) -> Set[Type]:
+        return {tp for element in self.elements.values() for tp in element.types}
+
     def get_xml_type(self) -> str:
         if self.is_wildcard:
             return XmlType.WILDCARD
@@ -207,10 +213,10 @@ class XmlVar(MetaMixin):
             tokens = False
             check_subclass = is_dataclass(value)
 
-        return self.find_type_choice(tp, tokens, check_subclass)
+        return self.find_type_choice(value, tp, tokens, check_subclass)
 
     def find_type_choice(
-        self, tp: Type, tokens: bool, check_subclass: bool
+        self, value: Any, tp: Type, tokens: bool, check_subclass: bool
     ) -> Optional["XmlVar"]:
         """Match and return a choice field that matches the given type."""
 
@@ -222,18 +228,24 @@ class XmlVar(MetaMixin):
             if tp is NoneType:
                 if element.nillable:
                     return element
-            elif self.match_type(tp, element.types, check_subclass):
+            elif self.match_type(value, tp, element.types, check_subclass):
                 return element
 
         return None
 
     @classmethod
-    def match_type(cls, tp: Type, types: Sequence[Type], check_subclass: bool) -> bool:
+    def match_type(
+        cls, value: Any, tp: Type, types: Sequence[Type], check_subclass: bool
+    ) -> bool:
+
         for candidate in types:
             if tp == candidate or (check_subclass and issubclass(tp, candidate)):
                 return True
 
-        return False
+        if isinstance(value, list):
+            return all(converter.test(val, types) for val in value)
+
+        return converter.test(value, types)
 
     def match_namespace(self, qname: str) -> bool:
         """Match the given qname to the wildcard allowed namespaces."""
@@ -416,6 +428,15 @@ class XmlMeta(MetaMixin):
         if chd:
             yield chd
 
+    @property
+    def element_types(self) -> Set[Type]:
+        return {
+            tp
+            for elements in self.elements.values()
+            for element in elements
+            for tp in element.types
+        }
+
     def __repr__(self) -> str:
         params = (
             f"clazz={self.clazz}, "
diff --git a/xsdata/formats/dataclass/models/generics.py b/xsdata/formats/dataclass/models/generics.py
index 2f3461b4..b4c60d89 100644
--- a/xsdata/formats/dataclass/models/generics.py
+++ b/xsdata/formats/dataclass/models/generics.py
@@ -43,9 +43,9 @@ class DerivedElement(Generic[T]):
 
     :param qname: The element's qualified name
     :param value: The wrapped value
-    :param substituted: Specify whether the value is a type substitution
+    :param type: The real xsi:type
     """
 
     qname: str
     value: T
-    substituted: bool = False
+    type: Optional[str] = None
diff --git a/xsdata/formats/dataclass/parsers/json.py b/xsdata/formats/dataclass/parsers/json.py
index 006b94ec..f6bd20fc 100644
--- a/xsdata/formats/dataclass/parsers/json.py
+++ b/xsdata/formats/dataclass/parsers/json.py
@@ -7,6 +7,7 @@ from dataclasses import is_dataclass
 from typing import Any
 from typing import Callable
 from typing import Dict
+from typing import Iterable
 from typing import List
 from typing import Optional
 from typing import Sequence
@@ -18,10 +19,14 @@ from xsdata.exceptions import ParserError
 from xsdata.formats.bindings import AbstractParser
 from xsdata.formats.bindings import T
 from xsdata.formats.dataclass.context import XmlContext
+from xsdata.formats.dataclass.models.elements import XmlMeta
 from xsdata.formats.dataclass.models.elements import XmlVar
 from xsdata.formats.dataclass.models.generics import AnyElement
 from xsdata.formats.dataclass.models.generics import DerivedElement
+from xsdata.formats.dataclass.parsers.config import ParserConfig
 from xsdata.formats.dataclass.parsers.utils import ParserUtils
+from xsdata.formats.dataclass.typing import get_args
+from xsdata.formats.dataclass.typing import get_origin
 from xsdata.utils.constants import EMPTY_MAP
 
 ANY_KEYS = {f.name for f in fields(AnyElement)}
@@ -33,10 +38,12 @@ class JsonParser(AbstractParser):
     """
     Json parser for dataclasses.
 
+    :param config: Parser configuration
     :param context: Model context provider
     :param load_factory: Replace the default json.load call with another implementation
     """
 
+    config: ParserConfig = field(default_factory=ParserConfig)
     context: XmlContext = field(default_factory=XmlContext)
     load_factory: Callable = field(default=json.load)
 
@@ -63,10 +70,21 @@ class JsonParser(AbstractParser):
         if clazz is None:
             return self.detect_type(data)
 
-        origin = getattr(clazz, "__origin__", None)
-        list_type = origin in (list, List) or clazz is List
-        if origin is not None and not list_type:
-            raise ParserError(f"Origin {origin} is not supported")
+        try:
+            origin = get_origin(clazz)
+            list_type = False
+            if origin is List:
+                list_type = True
+                args = get_args(clazz)
+
+                if len(args) != 1 or not is_dataclass(args[0]):
+                    raise TypeError()
+
+                clazz = args[0]
+            elif origin is not None:
+                raise TypeError()
+        except TypeError:
+            raise ParserError(f"Invalid clazz argument: {clazz}")
 
         if list_type != isinstance(data, list):
             if list_type:
@@ -74,187 +92,211 @@ class JsonParser(AbstractParser):
             else:
                 raise ParserError("Document is array, expected object")
 
-        if list_type:
-            args = getattr(clazz, "__args__", ())
-            if args is None or len(args) != 1 or not is_dataclass(args[0]):
-                raise ParserError("List argument must be a dataclass")
-
-            clazz = args[0]
-
         return clazz  # type: ignore
 
     def detect_type(self, data: Union[Dict, List]) -> Type[T]:
         if not data:
             raise ParserError("Document is empty, can not detect type")
 
-        keys = list(data[0].keys() if isinstance(data, list) else data.keys())
+        keys = data[0].keys() if isinstance(data, list) else data.keys()
         clazz: Optional[Type[T]] = self.context.find_type_by_fields(set(keys))
 
-        if clazz is None:
-            raise ParserError(f"No class found matching the document keys({keys})")
-
-        return clazz
-
-    def bind_value(self, var: XmlVar, value: Any) -> Any:
-        """Bind value according to the class var."""
-
-        if var.is_attributes:
-            return dict(value)
-
-        if var.is_clazz_union:
-            if isinstance(value, dict):
-                return self.bind_dataclass_union(value, var)
-
-            return self.bind_type_union(value, var)
-
-        if var.clazz:
-            return self.bind_dataclass(value, var.clazz)
+        if clazz:
+            return clazz
 
-        if var.is_elements:
-            return self.bind_choice(value, var)
-
-        if var.is_wildcard or var.any_type:
-            return self.bind_wildcard(value)
-
-        return self.parse_value(value, var.types, var.default, var.tokens, var.format)
+        raise ParserError(f"Unable to locate model with properties({list(keys)})")
 
     def bind_dataclass(self, data: Dict, clazz: Type[T]) -> T:
         """Recursively build the given model from the input dict data."""
-        params = {}
-        for var in self.context.build(clazz).get_all_vars():
-            value = data.get(var.local_name)
 
-            if value is None or not var.init:
-                continue
+        if set(data.keys()) == DERIVED_KEYS:
+            return self.bind_derived_dataclass(data, clazz)
 
-            if var.list_element:
-                if not isinstance(value, list):
-                    raise ParserError(f"Key `{var.name}` value is not iterable")
+        meta = self.context.build(clazz)
+        xml_vars = meta.get_all_vars()
 
-                params[var.name] = [self.bind_value(var, val) for val in value]
-            else:
-                params[var.name] = self.bind_value(var, value)
+        params = {}
+        for key, value in data.items():
+            is_list = isinstance(value, list)
+            var = self.find_var(xml_vars, key, is_list)
 
-        return clazz(**params)  # type: ignore
+            if var is None and self.config.fail_on_unknown_properties:
+                raise ParserError(f"Unknown property {clazz.__qualname__}.{key}")
 
-    def maybe_bind_dataclass(self, data: Dict, clazz: Type[T]) -> Optional[T]:
+            if var and var.init:
+                params[var.name] = self.bind_value(meta, var, value)
+
+        try:
+            return clazz(**params)  # type: ignore
+        except TypeError as e:
+            raise ParserError(e)
+
+    def bind_derived_dataclass(self, data: Dict, clazz: Type[T]) -> Any:
+        qname = data["qname"]
+        xsi_type = data["type"]
+        params = data["value"]
+
+        if clazz is DerivedElement:
+            real_clazz: Optional[Type[T]] = None
+            if xsi_type:
+                real_clazz = self.context.find_type(xsi_type)
+
+            if real_clazz is None:
+                raise ParserError(
+                    f"Unable to locate derived model "
+                    f"with properties({list(params.keys())})"
+                )
+
+            value = self.bind_dataclass(params, real_clazz)
+        else:
+            value = self.bind_dataclass(params, clazz)
+
+        return DerivedElement(qname=qname, type=xsi_type, value=value)
+
+    def bind_best_dataclass(self, data: Dict, classes: Iterable[Type[T]]) -> T:
+        """Attempt to bind the given data to one possible models, if more than
+        one is successful return the object with the highest score."""
+        obj = None
+        keys = set(data.keys())
+        max_score = -1.0
+        for clazz in classes:
+            if is_dataclass(clazz) and self.context.local_names_match(keys, clazz):
+                candidate = self.bind_optional_dataclass(data, clazz)
+                score = ParserUtils.score_object(candidate)
+                if score > max_score:
+                    max_score = score
+                    obj = candidate
+
+        if obj:
+            return obj
+
+        raise ParserError(
+            f"Failed to bind object with properties({list(data.keys())}) "
+            f"to any of the {[cls.__qualname__ for cls in classes]}"
+        )
+
+    def bind_optional_dataclass(self, data: Dict, clazz: Type[T]) -> Optional[T]:
         """Recursively build the given model from the input dict data but fail
         on any converter warnings."""
         try:
             with warnings.catch_warnings():
                 warnings.filterwarnings("error", category=ConverterWarning)
                 return self.bind_dataclass(data, clazz)
-        except ConverterWarning:
+        except Exception:
             return None
 
-    def bind_dataclass_union(self, value: Dict, var: XmlVar) -> Any:
-        """Bind data to all possible models and return the best candidate."""
-        obj = None
-        max_score = -1.0
-        for clazz in var.types:
-            if not is_dataclass(clazz):
-                continue
-
-            candidate = self.maybe_bind_dataclass(value, clazz)
-            score = ParserUtils.score_object(candidate)
-            if score > max_score:
-                max_score = score
-                obj = candidate
-
-        return obj
-
-    def bind_type_union(self, value: Any, var: XmlVar) -> Any:
-        types = [tp for tp in var.types if not is_dataclass(tp)]
-        return self.parse_value(value, types, var.default, var.tokens, var.format)
-
-    def bind_wildcard(self, value: Any) -> Any:
-        """Bind data to a wildcard model."""
-        if isinstance(value, Dict):
-            keys = set(value.keys())
-
-            if not (keys - ANY_KEYS):
-                return self.bind_dataclass(value, AnyElement)
-
-            if not (keys - DERIVED_KEYS):
-                return self.bind_dataclass(value, DerivedElement)
+    def bind_value(
+        self, meta: XmlMeta, var: XmlVar, value: Any, recursive: bool = False
+    ) -> Any:
+        """Main entry point for binding values."""
 
-            clazz: Optional[Type] = self.context.find_type_by_fields(keys)
-            if clazz:
-                return self.bind_dataclass(value, clazz)
+        # xs:anyAttributes get it out of the way, it's the mapping exception!
+        if var.is_attributes:
+            return dict(value)
 
-        return value
+        # Repeating element, recursively bind the values
+        if not recursive and var.list_element and isinstance(value, list):
+            return [self.bind_value(meta, var, val, True) for val in value]
 
-    def bind_choice(self, value: Any, var: XmlVar) -> Any:
-        """Bind data to one of the choice models."""
+        # If not dict this is an text or tokens value.
         if not isinstance(value, dict):
-            return self.bind_choice_simple(value, var)
+            return self.bind_text(meta, var, value)
 
-        if "qname" in value:
-            return self.bind_choice_generic(value, var)
+        keys = value.keys()
+        if keys == ANY_KEYS:
+            # Bind data to AnyElement dataclass
+            return self.bind_dataclass(value, AnyElement)
 
-        return self.bind_choice_dataclass(value, var)
+        if keys == DERIVED_KEYS:
+            # Bind data to AnyElement dataclass
+            return self.bind_derived_value(meta, var, value)
 
-    def bind_choice_simple(self, value: Any, var: XmlVar) -> Any:
-        """Bind data to one of the simple choice types and return the first
-        that succeeds."""
-        choice = var.find_value_choice(value)
-        if choice:
-            return self.bind_value(choice, value)
+        # Bind data to a user defined dataclass
+        return self.bind_complex_type(meta, var, value)
 
-        # Sometimes exact type match doesn't work, eg Decimals, try all of them
-        is_list = isinstance(value, list)
-        for choice in var.elements.values():
-            if choice.clazz or choice.tokens != is_list:
-                continue
+    def bind_text(self, meta: XmlMeta, var: XmlVar, value: Any) -> Any:
+        """Bind text/tokens value entrypoint."""
 
-            with warnings.catch_warnings(record=True) as w:
-                result = self.bind_value(choice, value)
-                if not w:
-                    return result
+        if var.elements:
+            # Compound field we need to match the value to one of the choice elements
+            choice = var.find_value_choice(value)
+            if choice:
+                return self.bind_text(meta, choice, value)
 
-        return value
-
-    def bind_choice_generic(self, value: Dict, var: XmlVar) -> Any:
-        """Bind data to a either a derived or a user derived model."""
-        qname = value["qname"]
-        choice = var.find_choice(qname)
-
-        if not choice:
             raise ParserError(
-                f"XmlElements undefined choice: `{var.name}` for qname `{qname}`"
+                f"Failed to bind '{value}' "
+                f"to {meta.clazz.__qualname__}.{var.name} field"
             )
 
-        if "value" in value:
-            obj = self.bind_value(choice, value["value"])
-            substituted = value.get("substituted", False)
-
-            return DerivedElement(qname=qname, value=obj, substituted=substituted)
+        if var.any_type or var.is_wildcard:
+            # field can support any object return the value as it is
+            return value
 
-        return self.bind_dataclass(value, AnyElement)
+        # Convert value according to the field types
+        return ParserUtils.parse_value(
+            value, var.types, var.default, EMPTY_MAP, var.tokens, var.format
+        )
 
-    def bind_choice_dataclass(self, value: Dict, var: XmlVar) -> Any:
-        """Bind data to the best matching choice model."""
-        keys = set(value.keys())
-        for choice in var.elements.values():
-            if choice.clazz:
-                meta = self.context.build(choice.clazz)
-                attrs = {var.local_name for var in meta.get_all_vars()}
-                if attrs == keys:
-                    return self.bind_value(choice, value)
+    def bind_complex_type(self, meta: XmlMeta, var: XmlVar, data: Dict) -> Any:
+        """Bind data to a user defined dataclass."""
 
-        raise ParserError(f"XmlElements undefined choice: `{var.name}` for `{value}`")
+        if var.is_clazz_union:
+            # Union of dataclasses
+            return self.bind_best_dataclass(data, var.types)
+        elif var.elements:
+            # Compound field with multiple choices
+            return self.bind_best_dataclass(data, var.element_types)
+        elif var.any_type or var.is_wildcard:
+            # xs:anyType element, check all meta classes
+            return self.bind_best_dataclass(data, meta.element_types)
+        else:
+            return self.bind_dataclass(data, var.clazz)
+
+    def bind_derived_value(
+        self, meta: XmlMeta, var: XmlVar, data: Dict
+    ) -> DerivedElement:
+        """Bind derived element entry point."""
+
+        qname = data["qname"]
+        xsi_type = data["type"]
+        params = data["value"]
+
+        if var.elements:
+            choice = var.find_choice(qname)
+            if choice is None:
+                raise ParserError(
+                    f"Unable to locate compound element"
+                    f" {meta.clazz.__qualname__}.{var.name}[{qname}]"
+                )
+            return self.bind_derived_value(meta, choice, data)
+
+        if not isinstance(params, dict):
+            value = self.bind_text(meta, var, params)
+        elif var.clazz:
+            value = self.bind_complex_type(meta, var, params)
+        elif xsi_type:
+            clazz: Optional[Type] = self.context.find_type(xsi_type)
+
+            if clazz is None:
+                raise ParserError(f"Unable to locate xsi:type `{xsi_type}`")
+
+            value = self.bind_dataclass(params, clazz)
+        else:
+            value = self.bind_best_dataclass(params, meta.element_types)
+
+        return DerivedElement(qname=qname, value=value, type=xsi_type)
 
     @classmethod
-    def parse_value(
-        cls,
-        value: Any,
-        types: Sequence[Type],
-        default: Any,
-        tokens: bool,
-        fmt: Optional[str],
-    ) -> Any:
-        """Convert any value to one of the given var types."""
-        return ParserUtils.parse_value(value, types, default, EMPTY_MAP, tokens, fmt)
+    def find_var(
+        cls, xml_vars: Sequence[XmlVar], local_name: str, is_list: bool = False
+    ) -> Optional[XmlVar]:
+        for var in xml_vars:
+            if var.local_name == local_name:
+                var_is_list = var.list_element or var.tokens
+                if is_list == var_is_list or var.clazz is None:
+                    return var
+
+        return None
 
 
 @dataclass
diff --git a/xsdata/formats/dataclass/parsers/nodes.py b/xsdata/formats/dataclass/parsers/nodes.py
index 570a354a..8e335ce6 100644
--- a/xsdata/formats/dataclass/parsers/nodes.py
+++ b/xsdata/formats/dataclass/parsers/nodes.py
@@ -56,7 +56,7 @@ class ElementNode(XmlNode):
     position: int
     mixed: bool = False
     derived: bool = False
-    substituted: bool = False
+    xsi_type: Optional[str] = None
     assigned: Set = field(default_factory=set)
 
     def bind(self, qname: str, text: NoneStr, tail: NoneStr, objects: List) -> bool:
@@ -78,7 +78,7 @@ class ElementNode(XmlNode):
 
         obj = self.meta.clazz(**params)
         if self.derived:
-            obj = DerivedElement(qname=qname, value=obj, substituted=self.substituted)
+            obj = DerivedElement(qname=qname, value=obj, type=self.xsi_type)
 
         objects.append((qname, obj))
 
@@ -180,7 +180,7 @@ class ElementNode(XmlNode):
             context=self.context,
             position=position,
             derived=derived,
-            substituted=xsi_type is not None,
+            xsi_type=xsi_type,
             mixed=self.meta.mixed_content,
         )
 
@@ -486,6 +486,7 @@ class NodeParser(PushParser):
                 ns_map=ns_map,
                 context=self.context,
                 derived=derived,
+                xsi_type=xsi_type if derived else None,
             )
 
         queue.append(child)
diff --git a/xsdata/formats/dataclass/serializers/xml.py b/xsdata/formats/dataclass/serializers/xml.py
index 1c6967d0..15888d71 100644
--- a/xsdata/formats/dataclass/serializers/xml.py
+++ b/xsdata/formats/dataclass/serializers/xml.py
@@ -195,7 +195,7 @@ class XmlSerializer(AbstractSerializer):
     ) -> Generator:
         if is_dataclass(value.value):
             xsi_type = None
-            if value.substituted:
+            if value.type:
                 meta = self.context.build(value.value.__class__)
                 xsi_type = QName(meta.source_qname)
 
diff --git a/xsdata/formats/dataclass/typing.py b/xsdata/formats/dataclass/typing.py
index c5a674d1..d98fbe79 100644
--- a/xsdata/formats/dataclass/typing.py
+++ b/xsdata/formats/dataclass/typing.py
@@ -13,7 +13,11 @@ EMPTY_ARGS = ()
 
 
 def get_origin(tp: Any) -> Any:
-    if tp in (List, Dict, Union):
+
+    if tp is Dict:
+        return Dict
+
+    if tp in (List, Union):
         raise TypeError()
 
     if isinstance(tp, TypeVar):
@@ -69,10 +73,24 @@ def _evaluate(tp: Any) -> Iterator[Type]:
 def _evaluate_mapping(tp: Any) -> Iterator[Type]:
     yield dict
     args = get_args(tp)
+
+    if not args:
+        yield str
+        yield str
+
     for arg in args:
-        if get_origin(arg):
+        origin = get_origin(arg)
+        if origin is TypeVar:
+            try:
+                next(_evaluate_typevar(arg))
+            except TypeError:
+                yield str
+            else:
+                raise TypeError()
+        elif origin is not None:
             raise TypeError()
-        yield arg
+        else:
+            yield arg
 
 
 def _evaluate_list(tp: Any) -> Iterator[Type]:
diff --git a/xsdata/models/xsd.py b/xsdata/models/xsd.py
index b4a3a1d2..fcaf4c6c 100644
--- a/xsdata/models/xsd.py
+++ b/xsdata/models/xsd.py
@@ -863,7 +863,7 @@ class Restriction(AnnotationBase):
 
         if self.patterns:
             restrictions["pattern"] = "|".join(
-                [pattern.value for pattern in self.patterns]
+                pattern.value for pattern in self.patterns
             )
 
         return restrictions


Test patch:
diff --git a/tests/fixtures/models.py b/tests/fixtures/models.py
new file mode 100644
index 00000000..0272367a
--- /dev/null
+++ b/tests/fixtures/models.py
@@ -0,0 +1,61 @@
+from dataclasses import dataclass, field
+from typing import List, Dict, Union, Optional
+from xml.etree.ElementTree import QName
+
+
+@dataclass
+class TypeA:
+    x: int
+
+
+@dataclass
+class TypeB:
+    x: int
+    y: str
+
+
+@dataclass
+class TypeC:
+    x: int
+    y: str
+    z: float
+
+
+@dataclass
+class TypeD:
+    x: int
+    y: str
+    z: Optional[bool]
+
+
+@dataclass
+class ExtendedType:
+    a: Optional[TypeA] = field(default=None)
+    any: Optional[object] = field(default=None)
+    wildcard: Optional[object] = field(default=None, metadata={"type": "Wildcard"})
+
+
+@dataclass
+class ChoiceType:
+    choice: List[object] = field(metadata={
+        "type": "Elements",
+        "choices": (
+            {"name": "a", "type": TypeA},
+            {"name": "b", "type": TypeB},
+            {"name": "int", "type": int},
+            {"name": "int2", "type": int},
+            {"name": "float", "type": float},
+            {"name": "qname", "type": QName},
+            {"name": "tokens", "type": List[int], "tokens": True},
+        )
+    })
+
+
+@dataclass
+class UnionType:
+    element: Union[TypeA, TypeB, TypeC, TypeD]
+
+
+@dataclass
+class AttrsType:
+    attrs: Dict[str, str] = field(metadata={"type": "Attributes"})
diff --git a/tests/formats/dataclass/models/test_builders.py b/tests/formats/dataclass/models/test_builders.py
index f622a2fd..f523d91b 100644
--- a/tests/formats/dataclass/models/test_builders.py
+++ b/tests/formats/dataclass/models/test_builders.py
@@ -92,7 +92,7 @@ class XmlMetaBuilderTests(FactoryTestCase):
         with self.assertRaises(XmlContextError) as cm:
             XmlMetaBuilder.build(int, None, return_input, return_input)
 
-        self.assertEqual(f"Object {int} is not a dataclass.", str(cm.exception))
+        self.assertEqual(f"Type '{int}' is not a dataclass.", str(cm.exception))
 
     def test_build_vars(self):
         result = XmlMetaBuilder.build_vars(BookForm, None, text.pascal_case, str.upper)
diff --git a/tests/formats/dataclass/parsers/test_json.py b/tests/formats/dataclass/parsers/test_json.py
index 2045854d..1ee2d1ed 100644
--- a/tests/formats/dataclass/parsers/test_json.py
+++ b/tests/formats/dataclass/parsers/test_json.py
@@ -1,22 +1,26 @@
 import json
-from dataclasses import make_dataclass
 from typing import List
 from typing import Optional
-from unittest.case import TestCase
 from xml.etree.ElementTree import QName
 
 from tests import fixtures_dir
 from tests.fixtures.books import BookForm
 from tests.fixtures.books import Books
+from tests.fixtures.models import AttrsType
+from tests.fixtures.models import ChoiceType
+from tests.fixtures.models import ExtendedType
+from tests.fixtures.models import TypeA
+from tests.fixtures.models import TypeB
+from tests.fixtures.models import TypeC
+from tests.fixtures.models import TypeD
+from tests.fixtures.models import UnionType
 from xsdata.exceptions import ParserError
-from xsdata.formats.dataclass.models.elements import XmlType
 from xsdata.formats.dataclass.models.generics import AnyElement
 from xsdata.formats.dataclass.models.generics import DerivedElement
 from xsdata.formats.dataclass.parsers.json import JsonParser
 from xsdata.formats.dataclass.serializers import JsonSerializer
 from xsdata.models.datatype import XmlDate
 from xsdata.utils.testing import FactoryTestCase
-from xsdata.utils.testing import XmlVarFactory
 
 
 class JsonParserTests(FactoryTestCase):
@@ -99,19 +103,22 @@ class JsonParserTests(FactoryTestCase):
         self.assertIsInstance(book_list[1], BookForm)
 
     def test_verify_type(self):
-
         invalid_cases = [
             (
                 '{"not": 1, "found": 1}',
                 None,
-                "No class found matching the document keys(['not', 'found'])",
+                "Unable to locate model with properties(['not', 'found'])",
             ),
             ("{}", None, "Document is empty, can not detect type"),
             ("[]", BookForm, "Document is array, expected object"),
             ("{}", List[BookForm], "Document is object, expected array"),
-            ("{}", Optional[BookForm], "Origin typing.Union is not supported"),
-            ("[]", List[int], "List argument must be a dataclass"),
-            ("[]", List, "List argument must be a dataclass"),
+            (
+                "{}",
+                Optional[ChoiceType],
+                f"Invalid clazz argument: {Optional[ChoiceType]}",
+            ),
+            ("[]", List[int], f"Invalid clazz argument: {List[int]}"),
+            ("[]", List, f"Invalid clazz argument: {List}"),
         ]
 
         for json_string, clazz, exc_msg in invalid_cases:
@@ -121,219 +128,231 @@ class JsonParserTests(FactoryTestCase):
 
             self.assertEqual(exc_msg, str(cm.exception))
 
-    def test_parse_with_non_iterable_value(self):
+    def test_bind_dataclass_with_unknown_property(self):
+        data = {"unknown": True}
         with self.assertRaises(ParserError) as cm:
-            self.parser.from_string('{"book": 1}')
+            self.parser.bind_dataclass(data, Books)
 
-        self.assertEqual("Key `book` value is not iterable", str(cm.exception))
+        self.assertEqual("Unknown property Books.unknown", str(cm.exception))
 
-    def test_bind_value_with_attributes_var(self):
-        var = XmlVarFactory.create(xml_type=XmlType.ATTRIBUTES, name="a")
-        value = {"a": 1}
-        actual = self.parser.bind_value(var, value)
-        self.assertEqual(value, actual)
-        self.assertIsNot(value, actual)
+        self.parser.config.fail_on_unknown_properties = False
+        self.assertEqual(Books(), self.parser.bind_dataclass(data, Books))
 
-    def test_bind_dataclass_union(self):
-        a = make_dataclass("a", [("x", int), ("y", str)])
-        b = make_dataclass("b", [("x", int), ("y", str), ("z", float)])
-        c = make_dataclass("c", [("x", int), ("y", str), ("z", str)])
-        d = make_dataclass("d", [("x", int)])
-        var = XmlVarFactory.create(
-            xml_type=XmlType.ELEMENT,
-            name="union",
-            qname="union",
-            types=(a, b, c, int),
-        )
+    def test_bind_dataclass_with_required_fields(self):
+        obj = self.parser.bind_dataclass({"x": 1, "y": "a", "z": None}, TypeD)
 
-        data = {"x": 1, "y": "foo", "z": "foo"}
-        actual = self.parser.bind_value(var, data)
+        self.assertEqual(1, obj.x)
+        self.assertEqual("a", obj.y)
+        self.assertIsNone(obj.z)
 
-        self.assertIsInstance(actual, c)
+        with self.assertRaises(ParserError):
+            self.parser.bind_dataclass({"x": 1, "y": "a"}, TypeD)
 
-    def test_bind_type_union(self):
-        a = make_dataclass("a", [("x", int), ("y", str)])
-        var = XmlVarFactory.create(
-            xml_type=XmlType.ELEMENT,
-            name="union",
-            qname="union",
-            types=(a, int, float),
-        )
+    def test_bind_derived_dataclass(self):
+        data = {
+            "qname": "{urn:books}BookForm",
+            "type": None,
+            "value": {
+                "author": "Nagata, Suanne",
+                "title": "Becoming Somebody",
+            },
+        }
 
-        data = "1.1"
-        self.assertEqual(1.1, self.parser.bind_value(var, data))
-
-    def test_bind_choice_simple(self):
-        var = XmlVarFactory.create(
-            xml_type=XmlType.ELEMENTS,
-            qname="compound",
-            name="compound",
-            elements={
-                "int": XmlVarFactory.create(
-                    xml_type=XmlType.ELEMENT, qname="int", name="int", types=(int,)
-                ),
-                "tokens": XmlVarFactory.create(
-                    xml_type=XmlType.ELEMENT,
-                    qname="tokens",
-                    name="tokens",
-                    types=(int,),
-                    tokens=True,
-                ),
-                "float": XmlVarFactory.create(
-                    xml_type=XmlType.ELEMENT,
-                    qname="float",
-                    name="float",
-                    types=(float,),
-                ),
-                "qname": XmlVarFactory.create(
-                    xml_type=XmlType.ELEMENT,
-                    qname="qname",
-                    name="qname",
-                    types=(QName,),
-                ),
+        actual = self.parser.bind_dataclass(data, BookForm)
+        expected = DerivedElement(
+            qname="{urn:books}BookForm",
+            value=BookForm(author="Nagata, Suanne", title="Becoming Somebody"),
+            type=None,
+        )
+        self.assertEqual(expected, actual)
+
+    def test_bind_derived_dataclass_with_xsi_type(self):
+        data = {
+            "qname": "foobar",
+            "type": "{urn:books}BookForm",
+            "value": {
+                "author": "Nagata, Suanne",
+                "title": "Becoming Somebody",
             },
+        }
+
+        actual = self.parser.bind_dataclass(data, DerivedElement)
+        expected = DerivedElement(
+            qname="foobar",
+            value=BookForm(author="Nagata, Suanne", title="Becoming Somebody"),
+            type="{urn:books}BookForm",
         )
-        self.assertEqual(1.0, self.parser.bind_choice("1.0", var))
-        self.assertEqual(1, self.parser.bind_choice(1, var))
-        self.assertEqual([1], self.parser.bind_choice(["1"], var))
+        self.assertEqual(expected, actual)
 
-        actual = self.parser.bind_choice("a", var)
-        self.assertEqual(QName("a"), actual)
-        self.assertIsInstance(actual, QName)
+        with self.assertRaises(ParserError) as cm:
+            data["type"] = "notExists"
+            self.parser.bind_dataclass(data, DerivedElement)
 
-        actual = self.parser.bind_choice("{a}b", var)
-        self.assertIsInstance(actual, QName)
-        self.assertEqual(QName("{a}b"), actual)
+        self.assertEqual(
+            "Unable to locate derived model with" " properties(['author', 'title'])",
+            str(cm.exception),
+        )
 
-        actual = self.parser.bind_choice("!NotQName", var)
-        self.assertIsInstance(actual, str)
-        self.assertEqual("!NotQName", actual)
+        with self.assertRaises(ParserError) as cm:
+            data["type"] = None
+            self.parser.bind_dataclass(data, DerivedElement)
 
-    def test_bind_choice_dataclass(self):
-        a = make_dataclass("a", [("x", int)])
-        b = make_dataclass("b", [("x", int), ("y", str)])
-
-        var = XmlVarFactory.create(
-            xml_type=XmlType.ELEMENTS,
-            qname="compound",
-            name="compound",
-            elements={
-                "a": XmlVarFactory.create(
-                    xml_type=XmlType.ELEMENT,
-                    qname="a",
-                    name="a",
-                    types=(a,),
-                ),
-                "b": XmlVarFactory.create(
-                    xml_type=XmlType.ELEMENT,
-                    qname="b",
-                    name="b",
-                    types=(b,),
-                ),
-                "c": XmlVarFactory.create(
-                    xml_type=XmlType.ELEMENT, qname="c", name="c", types=(int,)
-                ),
-            },
-        )
+    def test_bind_dataclass_union(self):
+        data = {"element": {"x": 1, "y": "foo", "z": "1.0"}}
+        actual = self.parser.bind_dataclass(data, UnionType)
 
-        self.assertEqual(a(1), self.parser.bind_choice({"x": 1}, var))
-        self.assertEqual(b(1, "2"), self.parser.bind_choice({"x": 1, "y": "2"}, var))
+        self.assertIsInstance(actual.element, TypeC)
 
         with self.assertRaises(ParserError) as cm:
-            self.parser.bind_choice({"x": 1, "y": "2", "z": 3}, var)
+            self.parser.bind_dataclass({"element": {"a": 1}}, UnionType)
 
         self.assertEqual(
-            "XmlElements undefined choice: `compound` for `{'x': 1, 'y': '2', 'z': 3}`",
+            "Failed to bind object with properties(['a']) "
+            "to any of the ['TypeA', 'TypeB', 'TypeC', 'TypeD']",
             str(cm.exception),
         )
 
-    def test_bind_choice_generic_with_derived(self):
-        var = XmlVarFactory.create(
-            xml_type=XmlType.ELEMENTS,
-            qname="compound",
-            name="compound",
-            elements={
-                "a": XmlVarFactory.create(
-                    xml_type=XmlType.ELEMENT, name="a", types=(int,)
-                ),
-                "b": XmlVarFactory.create(
-                    xml_type=XmlType.ELEMENT, name="b", types=(float,)
-                ),
-            },
-        )
-        data = {"qname": "a", "value": 1, "substituted": True}
+    def test_bind_attributes(self):
+        data = {"attrs": {"a": 1, "b": 2}}
 
-        self.assertEqual(
-            DerivedElement(qname="a", value=1, substituted=True),
-            self.parser.bind_choice(data, var),
-        )
+        actual = self.parser.bind_dataclass(data, AttrsType)
+        self.assertEqual(data["attrs"], actual.attrs)
+        self.assertIsNot(data["attrs"], actual.attrs)
 
-    def test_bind_choice_generic_with_wildcard(self):
-        var = XmlVarFactory.create(
-            xml_type=XmlType.ELEMENTS,
-            qname="compound",
-            name="compound",
-            elements={
-                "a": XmlVarFactory.create(
-                    xml_type=XmlType.ELEMENT, name="a", types=(int,)
-                ),
-                "b": XmlVarFactory.create(
-                    xml_type=XmlType.ELEMENT, name="b", types=(float,)
-                ),
-            },
+    def test_bind_simple_type_with_wildcard_var(self):
+        data = {"any": 1, "wildcard": 2}
+        actual = self.parser.bind_dataclass(data, ExtendedType)
+        self.assertEqual(1, actual.any)
+        self.assertEqual(2, actual.wildcard)
+
+    def test_bind_simple_type_with_elements_var(self):
+        data = {"choice": ["1.0", 1, ["1"], "a", "{a}b"]}
+
+        actual = self.parser.bind_dataclass(data, ChoiceType)
+
+        self.assertEqual(1.0, actual.choice[0])
+        self.assertEqual(1, actual.choice[1])
+        self.assertEqual([1], actual.choice[2])
+        self.assertEqual(QName("a"), actual.choice[3])
+        self.assertIsInstance(actual.choice[3], QName)
+        self.assertEqual(QName("{a}b"), actual.choice[4])
+        self.assertIsInstance(actual.choice[4], QName)
+
+        data = {"choice": ["!NotAQname"]}
+        with self.assertRaises(ParserError) as cm:
+            self.parser.bind_dataclass(data, ChoiceType)
+
+        self.assertEqual(
+            "Failed to bind '!NotAQname' to ChoiceType.choice field",
+            str(cm.exception),
         )
 
+    def test_bind_any_element(self):
+        data = {
+            "wildcard": {
+                "qname": "a",
+                "text": 1,
+                "tail": None,
+                "children": [],
+                "attributes": {},
+            }
+        }
         self.assertEqual(
-            AnyElement(qname="a", text="1"),
-            self.parser.bind_choice({"qname": "a", "text": 1}, var),
+            ExtendedType(wildcard=AnyElement(qname="a", text="1")),
+            self.parser.bind_dataclass(data, ExtendedType),
         )
 
-    def test_bind_choice_generic_with_unknown_qname(self):
-        var = XmlVarFactory.create(
-            xml_type=XmlType.ELEMENTS, qname="compound", name="compound"
+    def test_bind_choice_dataclass(self):
+        data = {"choice": [{"x": 1}, {"x": 1, "y": "a"}]}
+        expected = ChoiceType(choice=[TypeA(x=1), TypeB(x=1, y="a")])
+        self.assertEqual(expected, self.parser.bind_dataclass(data, ChoiceType))
+
+    def test_bind_derived_value_with_simple_type(self):
+        data = {"choice": [{"qname": "int2", "value": 1, "type": None}]}
+
+        actual = self.parser.bind_dataclass(data, ChoiceType)
+        expected = ChoiceType(choice=[DerivedElement(qname="int2", value=1)])
+        self.assertEqual(expected, actual)
+
+    def test_bind_derived_value_with_choice_var(self):
+        data = {
+            "choice": [
+                {
+                    "qname": "b",
+                    "type": None,
+                    "value": {
+                        "x": "1",
+                        "y": "a",
+                    },
+                }
+            ]
+        }
+        expected = ChoiceType(
+            choice=[
+                DerivedElement(
+                    qname="b",
+                    value=TypeB(x=1, y="a"),
+                )
+            ]
         )
+        self.assertEqual(expected, self.parser.bind_dataclass(data, ChoiceType))
 
         with self.assertRaises(ParserError) as cm:
-            self.parser.bind_choice({"qname": "foo", "text": 1}, var)
+            data["choice"][0]["qname"] = "nope"
+            self.parser.bind_dataclass(data, ChoiceType)
 
         self.assertEqual(
-            "XmlElements undefined choice: `compound` for qname `foo`",
+            "Unable to locate compound element ChoiceType.choice[nope]",
             str(cm.exception),
         )
 
-    def test_bind_wildcard_with_any_element(self):
-        var = XmlVarFactory.create(
-            xml_type=XmlType.WILDCARD,
-            name="any_element",
-            qname="any_element",
-            types=(object,),
+    def test_bind_wildcard_dataclass(self):
+
+        data = {"a": None, "wildcard": {"x": 1}}
+        expected = ExtendedType(wildcard=TypeA(x=1))
+        self.assertEqual(expected, self.parser.bind_dataclass(data, ExtendedType))
+
+    def test_bind_wildcard_with_derived_dataclass(self):
+        data = {
+            "wildcard": {
+                "qname": "b",
+                "type": "TypeB",
+                "value": {
+                    "x": "1",
+                    "y": "a",
+                },
+            }
+        }
+        expected = ExtendedType(
+            wildcard=DerivedElement(qname="b", value=TypeB(x=1, y="a"), type="TypeB")
         )
+        self.assertEqual(expected, self.parser.bind_dataclass(data, ExtendedType))
+
+    def test_bind_any_type_with_derived_dataclass(self):
+        data = {
+            "any": {
+                "qname": "any",
+                "type": None,
+                "value": {"x": "1"},
+            }
+        }
+        expected = ExtendedType(any=DerivedElement(qname="any", value=TypeA(x=1)))
+        self.assertEqual(expected, self.parser.bind_dataclass(data, ExtendedType))
 
-        self.assertEqual(
-            AnyElement(qname="a", text="1"),
-            self.parser.bind_value(var, {"qname": "a", "text": 1}),
-        )
+        with self.assertRaises(ParserError) as cm:
+            data["any"]["type"] = "notexists"
+            self.parser.bind_dataclass(data, ExtendedType)
 
-    def test_bind_wildcard_with_derived_element(self):
-        var = XmlVarFactory.create(
-            any_type=True,
-            name="a",
-            qname="a",
-            types=(object,),
-        )
-        actual = DerivedElement(qname="a", value=Books(book=[]), substituted=True)
-        data = {"qname": "a", "value": {"book": []}, "substituted": True}
+        self.assertEqual("Unable to locate xsi:type `notexists`", str(cm.exception))
 
-        self.assertEqual(actual, self.parser.bind_value(var, data))
+    def test_find_var(self):
+        meta = self.parser.context.build(TypeB)
+        xml_vars = meta.get_all_vars()
 
-    def test_bind_wildcard_with_no_matching_value(self):
-        var = XmlVarFactory.create(
-            any_type=True,
-            name="a",
-            qname="a",
-            types=(object,),
-        )
+        self.assertEqual(xml_vars[0], self.parser.find_var(xml_vars, "x"))
+        self.assertEqual(xml_vars[0], self.parser.find_var(xml_vars, "x", True))
 
-        data = {"test_bind_wildcard_with_no_matching_value": False}
-        self.assertEqual(data, self.parser.bind_value(var, data))
-        self.assertEqual(1, self.parser.bind_value(var, 1))
+        meta = self.parser.context.build(ExtendedType)
+        xml_vars = meta.get_all_vars()
+        self.assertIsNone(self.parser.find_var(xml_vars, "a", True))
+        self.assertEqual(xml_vars[0], self.parser.find_var(xml_vars, "a"))
diff --git a/tests/formats/dataclass/parsers/test_nodes.py b/tests/formats/dataclass/parsers/test_nodes.py
index 97736719..7ef5c55f 100644
--- a/tests/formats/dataclass/parsers/test_nodes.py
+++ b/tests/formats/dataclass/parsers/test_nodes.py
@@ -791,6 +791,7 @@ class NodeParserTests(TestCase):
             attrs=attrs,
             ns_map=ns_map,
             derived=True,
+            xsi_type="{urn:books}books",
         )
         parser.start(None, queue, objects, "doc", attrs, ns_map)
         self.assertEqual(1, len(queue))
@@ -816,6 +817,7 @@ class NodeParserTests(TestCase):
             attrs=attrs,
             ns_map={},
             derived=True,
+            xsi_type="b",
         )
 
         self.assertEqual(1, len(queue))
diff --git a/tests/formats/dataclass/serializers/test_xml.py b/tests/formats/dataclass/serializers/test_xml.py
index 7491a323..cd8675d9 100644
--- a/tests/formats/dataclass/serializers/test_xml.py
+++ b/tests/formats/dataclass/serializers/test_xml.py
@@ -103,7 +103,7 @@ class XmlSerializerTests(TestCase):
     def test_write_dataclass_with_no_dataclass(self):
         with self.assertRaises(XmlContextError) as cm:
             next(self.serializer.write_dataclass(1))
-        self.assertEqual("Object <class 'int'> is not a dataclass.", str(cm.exception))
+        self.assertEqual("Type '<class 'int'>' is not a dataclass.", str(cm.exception))
 
     def test_write_mixed_content(self):
         var = XmlVarFactory.create(xml_type=XmlType.WILDCARD, qname="a", mixed=True)
@@ -238,7 +238,9 @@ class XmlSerializerTests(TestCase):
 
     def test_write_any_type_with_derived_element_dataclass(self):
         var = XmlVarFactory.create(xml_type=XmlType.WILDCARD, qname="a")
-        value = DerivedElement(qname="a", value=BookForm(title="def"), substituted=True)
+        value = DerivedElement(
+            qname="a", value=BookForm(title="def"), type="{urn:books}BookForm"
+        )
         expected = [
             (XmlWriterEvent.START, "a"),
             (XmlWriterEvent.ATTR, "lang", "en"),
diff --git a/tests/formats/dataclass/test_context.py b/tests/formats/dataclass/test_context.py
index e323d447..4dda83a2 100644
--- a/tests/formats/dataclass/test_context.py
+++ b/tests/formats/dataclass/test_context.py
@@ -71,9 +71,6 @@ class XmlContextTests(FactoryTestCase):
         field_names = {f.name for f in fields(BookForm)}
         self.assertEqual(BookForm, self.ctx.find_type_by_fields(field_names))
 
-        field_names.pop()  # Test matching less fields
-        self.assertEqual(BookForm, self.ctx.find_type_by_fields(field_names))
-
         field_names.update({"please", "dont", "exist"})  # Test matching with more
         self.assertIsNone(self.ctx.find_type_by_fields(field_names))
 
diff --git a/tests/formats/dataclass/test_elements.py b/tests/formats/dataclass/test_elements.py
index 8c4005d7..a78597ad 100644
--- a/tests/formats/dataclass/test_elements.py
+++ b/tests/formats/dataclass/test_elements.py
@@ -4,7 +4,16 @@ from dataclasses import make_dataclass
 from typing import Iterator
 from unittest import mock
 from unittest.case import TestCase
-
+from xml.etree.ElementTree import QName
+
+from tests.fixtures.models import ChoiceType
+from tests.fixtures.models import TypeA
+from tests.fixtures.models import TypeB
+from tests.fixtures.models import TypeC
+from tests.fixtures.models import TypeD
+from tests.fixtures.models import UnionType
+from xsdata.formats.dataclass.context import XmlContext
+from xsdata.formats.dataclass.models.builders import XmlMetaBuilder
 from xsdata.formats.dataclass.models.elements import XmlType
 from xsdata.formats.dataclass.models.elements import XmlVar
 from xsdata.utils.testing import XmlMetaFactory
@@ -17,6 +26,9 @@ class Fixture:
 
 
 class XmlValTests(TestCase):
+    def setUp(self) -> None:
+        self.context = XmlContext()
+
     def test_property_local_name(self):
         var = XmlVarFactory.create(name="a", qname="{B}A")
         self.assertEqual("A", var.local_name)
@@ -40,6 +52,11 @@ class XmlValTests(TestCase):
         var = XmlVarFactory.create(name="foo", types=(Fixture, int))
         self.assertTrue(var.is_clazz_union)
 
+    def test_property_element_types(self):
+        meta = self.context.build(ChoiceType)
+        var = meta.choices[0]
+        self.assertEqual({TypeA, TypeB, int, float, QName}, var.element_types)
+
     def test_find_choice(self):
         var = XmlVarFactory.create(
             xml_type=XmlType.ELEMENTS,
@@ -105,6 +122,7 @@ class XmlValTests(TestCase):
         )
 
         self.assertIsNone(var.find_value_choice("foo"))
+        self.assertIsNone(var.find_value_choice(["1.1", "1.2"]))
         self.assertEqual(elements[0], var.find_value_choice(1))
         self.assertEqual(elements[1], var.find_value_choice([1, 2]))
         self.assertEqual(elements[2], var.find_value_choice(d()))
@@ -182,6 +200,7 @@ class XmlValTests(TestCase):
 class XmlMetaTests(TestCase):
     def setUp(self) -> None:
         a = make_dataclass("a", [])
+        self.context = XmlContext()
         self.meta = XmlMetaFactory.create(
             clazz=a,
             qname="a",
@@ -192,6 +211,10 @@ class XmlMetaTests(TestCase):
             any_attributes=[],
         )
 
+    def test_property_element_types(self):
+        meta = self.context.build(UnionType)
+        self.assertEqual({TypeA, TypeB, TypeC, TypeD}, meta.element_types)
+
     def test_find_attribute(self):
         a = XmlVarFactory.create(xml_type=XmlType.ATTRIBUTE, name="a")
         b = XmlVarFactory.create(xml_type=XmlType.ATTRIBUTE, name="b")
diff --git a/tests/formats/dataclass/test_typing.py b/tests/formats/dataclass/test_typing.py
index 8755fc5c..ffa1b6c6 100644
--- a/tests/formats/dataclass/test_typing.py
+++ b/tests/formats/dataclass/test_typing.py
@@ -34,12 +34,10 @@ class TypingTests(TestCase):
             get_origin(List)
 
     def test_get_origin_dict(self):
+        self.assertEqual(Dict, get_origin(Dict))
         self.assertEqual(Dict, get_origin(Dict[int, str]))
         self.assertEqual(Dict, get_origin(Dict[Union[int], Union[str]]))
 
-        with self.assertRaises(TypeError):
-            get_origin(Dict)
-
     def test_get_origin_union(self):
         self.assertIsNone(get_origin(Union[int]))
         self.assertEqual(Union, get_origin(Optional[int]))
@@ -92,12 +90,14 @@ class TypingTests(TestCase):
         self.assertEqual((int,), evaluate(int))
 
     def test_evaluate_dict(self):
+        self.assertEqual((dict, str, str), evaluate(Dict))
         self.assertEqual((dict, str, int), evaluate(Dict[str, int]))
 
         unsupported_cases = [
             Dict[Any, Any],
             Dict[Union[str, int], int],
             Dict[int, Union[str, int]],
+            Dict[TypeVar("A", bound=int), str],
         ]
 
         for case in unsupported_cases:
diff --git a/tests/formats/test_converter.py b/tests/formats/test_converter.py
index 276c7dae..768c9287 100644
--- a/tests/formats/test_converter.py
+++ b/tests/formats/test_converter.py
@@ -114,17 +114,16 @@ class BoolConverterTests(TestCase):
         self.converter = converter.type_converter(bool)
 
     def test_deserialize(self):
-        with self.assertRaises(ConverterError):
-            self.converter.deserialize("True")
+        for invalid in ("True", "False", 1, 0):
+            with self.assertRaises(ConverterError):
+                self.converter.deserialize(invalid)
 
         self.assertTrue(self.converter.deserialize("true"))
         self.assertTrue(self.converter.deserialize("1"))
         self.assertFalse(self.converter.deserialize("false"))
         self.assertFalse(self.converter.deserialize("0"))
         self.assertTrue(self.converter.deserialize(True))
-        self.assertTrue(self.converter.deserialize(1))
         self.assertFalse(self.converter.deserialize(False))
-        self.assertFalse(self.converter.deserialize(0))
 
     def test_serialize(self):
         self.assertEqual("true", self.converter.serialize(True))