Repository: tefra/xsdata
Pull request #335 (2020-12-01 21:39:45)

Add dynamic root class loader for parsers

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


codecov[bot]: # [Codecov](https://codecov.io/gh/tefra/xsdata/pull/335?src=pr&el=h1) Report
> Merging [#335](https://codecov.io/gh/tefra/xsdata/pull/335?src=pr&el=desc) (3d2285c) into [master](https://codecov.io/gh/tefra/xsdata/commit/baa086961f0d96783dc3c29f93722306160f4059?el=desc) (baa0869) will **decrease** coverage by `0.08%`.
> The diff coverage is `83.33%`.

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

```diff
@@             Coverage Diff             @@
##            master     #335      +/-   ##
===========================================
- Coverage   100.00%   99.91%   -0.09%     
===========================================
  Files           70       70              
  Lines         5655     5676      +21     
  Branches       980      988       +8     
===========================================
+ Hits          5655     5671      +16     
- Misses           0        2       +2     
- Partials         0        3       +3     
```


| [Impacted Files](https://codecov.io/gh/tefra/xsdata/pull/335?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [xsdata/formats/dataclass/parsers/xml.py](https://codecov.io/gh/tefra/xsdata/pull/335/diff?src=pr&el=tree#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL3BhcnNlcnMveG1sLnB5) | `100.00% <ø> (ø)` | |
| [xsdata/formats/dataclass/context.py](https://codecov.io/gh/tefra/xsdata/pull/335/diff?src=pr&el=tree#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL2NvbnRleHQucHk=) | `98.55% <70.00%> (-1.45%)` | :arrow_down: |
| [xsdata/formats/dataclass/parsers/json.py](https://codecov.io/gh/tefra/xsdata/pull/335/diff?src=pr&el=tree#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL3BhcnNlcnMvanNvbi5weQ==) | `98.23% <71.42%> (-1.77%)` | :arrow_down: |
| [xsdata/formats/bindings.py](https://codecov.io/gh/tefra/xsdata/pull/335/diff?src=pr&el=tree#diff-eHNkYXRhL2Zvcm1hdHMvYmluZGluZ3MucHk=) | `100.00% <100.00%> (ø)` | |
| [xsdata/formats/dataclass/parsers/mixins.py](https://codecov.io/gh/tefra/xsdata/pull/335/diff?src=pr&el=tree#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL3BhcnNlcnMvbWl4aW5zLnB5) | `100.00% <100.00%> (ø)` | |
| [xsdata/formats/dataclass/parsers/nodes.py](https://codecov.io/gh/tefra/xsdata/pull/335/diff?src=pr&el=tree#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL3BhcnNlcnMvbm9kZXMucHk=) | `100.00% <100.00%> (ø)` | |

------

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

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

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=335&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=335&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=335&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=335) 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=335&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=335&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=335) 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=335&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=335&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=335) 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=335&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=335&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=335) 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=335&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=335&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=335&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=335&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=335&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=335) 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=335&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=335&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/docs/examples/xml_parser.py b/docs/examples/xml_parser.py
index ae956cad..6d6ed779 100644
--- a/docs/examples/xml_parser.py
+++ b/docs/examples/xml_parser.py
@@ -24,4 +24,7 @@ actual = parser.from_bytes(path.read_bytes(), Books)
 from xsdata.formats.dataclass.parsers.handlers import XmlEventHandler
 
 parser = XmlParser(handler=XmlEventHandler)
-order = parser.from_path(fixtures_dir.joinpath("primer/order.xml"), PurchaseOrder)
\ No newline at end of file
+order = parser.from_path(fixtures_dir.joinpath("primer/order.xml"), PurchaseOrder)
+
+order = parser.from_path(fixtures_dir.joinpath("primer/order.xml"))
+assert isinstance(order, PurchaseOrder)
\ No newline at end of file
diff --git a/docs/json.rst b/docs/json.rst
index bc1dfe02..b86872ca 100644
--- a/docs/json.rst
+++ b/docs/json.rst
@@ -2,15 +2,29 @@
 JSON Binding
 ============
 
-The json implementation lacks a bit in maturity but it has a similar interface to the
-xml implementation.
-
 
 :class:`~xsdata.formats.dataclass.parsers.JsonParser`
 =====================================================
 
-The parser also uses the :class:`~xsdata.formats.dataclass.context.XmlContext`
-for model metadata caching which can be shared across instances.
+The parser has three instance methods `from_string`, `from_bytes` and `from_path`,
+to parse from memory or to let the parser load the input document.
+
+.. hint::
+
+    You can optionally specify the target binding class or let the context instance
+    to scan all imported modules for a matching dataclass.
+
+**Parameters**
+    **context** (:class:`~xsdata.formats.dataclass.context.XmlContext`)
+
+    The cache layer for the binding directives of models and their fields. You may
+    share a context instance between parser/serializer instances to avoid compiling the
+    cache more than once.
+
+    .. hint::
+
+        it's recommended to use a static or global instance of your parser or serializer
+        per document type.
 
 .. code-block:: python
 
diff --git a/docs/xml.rst b/docs/xml.rst
index 4c8f739b..47ba1e30 100644
--- a/docs/xml.rst
+++ b/docs/xml.rst
@@ -7,9 +7,12 @@ XML Binding
 ====================================================
 
 The parser has three instance methods `from_string`, `from_bytes` and `from_path`,
-to parse from memory or to let the parser load the input document. All of them require
-the target class Type to bind the input data.
+to parse from memory or to let the parser load the input document.
 
+.. hint::
+
+    You can optionally specify the target binding class or let the context instance
+    to scan all imported modules for a matching dataclass.
 
 **Parameters**
     **config** (:class:`~xsdata.formats.dataclass.parsers.config.ParserConfig`)
@@ -123,6 +126,12 @@ Example: alternative handler
    :lines: 24-27
 
 
+Example: with unknown type
+--------------------------
+
+.. literalinclude:: examples/xml_parser.py
+   :lines: 29-30
+
 :class:`~xsdata.formats.dataclass.serializers.XmlSerializer`
 ============================================================
 
diff --git a/xsdata/codegen/parsers/schema.py b/xsdata/codegen/parsers/schema.py
index 26cee222..afcedccc 100644
--- a/xsdata/codegen/parsers/schema.py
+++ b/xsdata/codegen/parsers/schema.py
@@ -46,7 +46,7 @@ class SchemaParser(XmlParser):
 
     def start(
         self,
-        clazz: Type,
+        clazz: Optional[Type[T]],
         queue: List[XmlNode],
         objects: List[Parsed],
         qname: str,
diff --git a/xsdata/formats/bindings.py b/xsdata/formats/bindings.py
index 2707c50c..da446b83 100644
--- a/xsdata/formats/bindings.py
+++ b/xsdata/formats/bindings.py
@@ -2,6 +2,7 @@ import abc
 import io
 import pathlib
 from typing import Any
+from typing import Optional
 from typing import Type
 from typing import TypeVar
 
@@ -15,18 +16,18 @@ class AbstractSerializer(abc.ABC):
 
 
 class AbstractParser(abc.ABC):
-    def from_path(self, path: pathlib.Path, clazz: Type[T]) -> T:
+    def from_path(self, path: pathlib.Path, clazz: Optional[Type[T]] = None) -> T:
         """Parse the input file path and return the resulting object tree."""
         return self.parse(str(path.resolve()), clazz)
 
-    def from_string(self, source: str, clazz: Type[T]) -> T:
+    def from_string(self, source: str, clazz: Optional[Type[T]] = None) -> T:
         """Parse the input string and return the resulting object tree."""
         return self.from_bytes(source.encode(), clazz)
 
-    def from_bytes(self, source: bytes, clazz: Type[T]) -> T:
+    def from_bytes(self, source: bytes, clazz: Optional[Type[T]] = None) -> T:
         """Parse the input bytes array return the resulting object tree."""
         return self.parse(io.BytesIO(source), clazz)
 
     @abc.abstractmethod
-    def parse(self, source: Any, clazz: Type[T]) -> T:
+    def parse(self, source: Any, clazz: Optional[Type[T]] = None) -> T:
         """Parse the input stream and return the resulting object tree."""
diff --git a/xsdata/formats/dataclass/context.py b/xsdata/formats/dataclass/context.py
index 3ca4ca5c..23a747af 100644
--- a/xsdata/formats/dataclass/context.py
+++ b/xsdata/formats/dataclass/context.py
@@ -14,9 +14,11 @@ from typing import get_type_hints
 from typing import Iterator
 from typing import List
 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.converter import converter
 from xsdata.formats.dataclass.models.constants import XmlType
 from xsdata.formats.dataclass.models.elements import XmlMeta
@@ -70,6 +72,10 @@ class XmlContext:
 
     def build_xsi_cache(self):
         """Index all imported dataclasses by their xsi:type qualified name."""
+
+        if len(sys.modules) == self.sys_modules:
+            return
+
         self.xsi_cache.clear()
 
         for clazz in self.get_subclasses(object):
@@ -81,39 +87,47 @@ class XmlContext:
                 source_qname = build_qname(source_namespace, name)
                 self.xsi_cache[source_qname].append(clazz)
 
-    def find_types(self, qname: str) -> Optional[List[Type]]:
+        self.sys_modules = len(sys.modules)
+
+    def find_types(self, qname: str) -> List[Type[T]]:
         """
         Find all classes that match the given xsi:type qname.
 
         - Ignores native schema types, xs:string, xs:float, xs:int, ...
         - Rebuild cache if new modules were imported since last run
         """
-        if DataType.from_qname(qname):
-            return None
-
-        if len(sys.modules) != self.sys_modules:
+        if not DataType.from_qname(qname):
             self.build_xsi_cache()
-            self.sys_modules = len(sys.modules)
+            if qname in self.xsi_cache:
+                return self.xsi_cache[qname]
 
-        return self.xsi_cache[qname] if qname in self.xsi_cache else None
+        return []
 
-    def find_type(self, qname: str) -> Optional[Type]:
+    def find_type(self, qname: str) -> Optional[Type[T]]:
         """Return the most recently imported class that matches the given
         xsi:type qname."""
-        types = self.find_types(qname)
+        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]]:
+        self.build_xsi_cache()
+        for types in self.xsi_cache.values():
+            for clazz in types:
+                if field_names == {attr.name for attr in fields(clazz)}:
+                    return clazz
+
+        return None
+
     def find_subclass(self, clazz: Type, qname: str) -> Optional[Type]:
         """Compare all classes that match the given xsi:type qname and return
         the first one that is either a subclass or shares the same parent class
         as the original class."""
 
-        types = self.find_types(qname)
-        if types:
-            for tp in types:
-                for tp_mro in tp.__mro__:
-                    if tp_mro is not object and tp_mro in clazz.__mro__:
-                        return tp
+        types: List[Type] = self.find_types(qname)
+        for tp in types:
+            for tp_mro in tp.__mro__:
+                if tp_mro is not object and tp_mro in clazz.__mro__:
+                    return tp
 
         return None
 
diff --git a/xsdata/formats/dataclass/parsers/json.py b/xsdata/formats/dataclass/parsers/json.py
index a3b60665..133d99ad 100644
--- a/xsdata/formats/dataclass/parsers/json.py
+++ b/xsdata/formats/dataclass/parsers/json.py
@@ -7,6 +7,7 @@ from dataclasses import field
 from dataclasses import fields
 from typing import Any
 from typing import Dict
+from typing import Optional
 from typing import Type
 
 from xsdata.exceptions import ParserError
@@ -29,12 +30,22 @@ class JsonParser(AbstractParser):
 
     context: XmlContext = field(default_factory=XmlContext)
 
-    def from_path(self, path: pathlib.Path, clazz: Type[T]) -> T:
+    def from_path(self, path: pathlib.Path, clazz: Optional[Type[T]] = None) -> T:
+        """Parse the input file path and return the resulting object tree."""
         return self.from_bytes(path.read_bytes(), clazz)
 
-    def parse(self, source: io.BytesIO, clazz: Type[T]) -> T:
+    def parse(self, source: io.BytesIO, clazz: Optional[Type[T]] = None) -> T:
         """Parse the JSON input stream and return the resulting object tree."""
         ctx = json.load(source)
+
+        if clazz is None:
+            clazz = self.context.find_type_by_fields(set(ctx.keys()))
+
+        if clazz is None:
+            raise ParserError(
+                f"No class found matching the document keys({list(ctx.keys())})"
+            )
+
         return self.bind_dataclass(ctx, clazz)
 
     def bind_value(self, var: XmlVar, value: Any) -> Any:
diff --git a/xsdata/formats/dataclass/parsers/mixins.py b/xsdata/formats/dataclass/parsers/mixins.py
index e330622e..ba3befc2 100644
--- a/xsdata/formats/dataclass/parsers/mixins.py
+++ b/xsdata/formats/dataclass/parsers/mixins.py
@@ -28,7 +28,7 @@ class PushParser(AbstractParser):
     @abc.abstractmethod
     def start(
         self,
-        clazz: Type,
+        clazz: Optional[Type],
         queue: List,
         objects: List,
         qname: str,
@@ -95,14 +95,15 @@ class XmlHandler:
     """
     Xml content handler interface.
 
-    :param clazz: The Dataclass model to bind the xml document data.
     :param parser: The parser instance to feed events.
+    :param clazz: The model to bind the xml document data. If it's not provided the
+        parser is responsible to locate one from the xml context.
     :param queue: The queue list of xml nodes.
     :param objects: Temporary storage for intermediate objects, eg [(qname, object)]
     """
 
-    clazz: Type
     parser: PushParser
+    clazz: Optional[Type]
     queue: List = field(default_factory=list)
     objects: List = field(default_factory=list)
 
diff --git a/xsdata/formats/dataclass/parsers/nodes.py b/xsdata/formats/dataclass/parsers/nodes.py
index 67b130e6..3e7c9c57 100644
--- a/xsdata/formats/dataclass/parsers/nodes.py
+++ b/xsdata/formats/dataclass/parsers/nodes.py
@@ -431,7 +431,7 @@ class NodeParser(PushParser):
     handler: Type[XmlHandler] = field(default=EventsHandler)
     ns_map: Dict = field(init=False, default_factory=dict)
 
-    def parse(self, source: Any, clazz: Type[T]) -> T:
+    def parse(self, source: Any, clazz: Optional[Type[T]] = None) -> T:
         """Parse the XML input stream and return the resulting object tree."""
         handler = self.handler(clazz=clazz, parser=self)
         result = handler.parse(source)
@@ -439,11 +439,12 @@ class NodeParser(PushParser):
         if result is not None:
             return result
 
-        raise ParserError(f"Failed to create target class `{clazz.__name__}`")
+        target_class = clazz.__name__ if clazz else ""
+        raise ParserError(f"Failed to create target class `{target_class}`")
 
     def start(
         self,
-        clazz: Type,
+        clazz: Optional[Type],
         queue: List[XmlNode],
         objects: List[Parsed],
         qname: str,
@@ -455,6 +456,13 @@ class NodeParser(PushParser):
             item = queue[-1]
             child = item.child(qname, attrs, ns_map, len(objects))
         except IndexError:
+
+            if clazz is None:
+                clazz = self.context.find_type(qname)
+
+            if clazz is None:
+                raise ParserError(f"No class found matching root: {qname}")
+
             xsi_type = ParserUtils.xsi_type(attrs, ns_map)
             meta = self.context.fetch(clazz, xsi_type=xsi_type)
             derived = xsi_type is not None and meta.qname != qname
@@ -510,7 +518,7 @@ class RecordParser(NodeParser):
 
     def start(
         self,
-        clazz: Type,
+        clazz: Optional[Type],
         queue: List[XmlNode],
         objects: List[Parsed],
         qname: str,
diff --git a/xsdata/formats/dataclass/parsers/xml.py b/xsdata/formats/dataclass/parsers/xml.py
index 3f59cd4c..a883bc78 100644
--- a/xsdata/formats/dataclass/parsers/xml.py
+++ b/xsdata/formats/dataclass/parsers/xml.py
@@ -30,7 +30,7 @@ class XmlParser(NodeParser):
 
     def start(
         self,
-        clazz: Type,
+        clazz: Optional[Type],
         queue: List[XmlNode],
         objects: List[Parsed],
         qname: str,


Test patch:
diff --git a/tests/formats/dataclass/parsers/test_json.py b/tests/formats/dataclass/parsers/test_json.py
index 31533bcb..63ed15c7 100644
--- a/tests/formats/dataclass/parsers/test_json.py
+++ b/tests/formats/dataclass/parsers/test_json.py
@@ -52,6 +52,20 @@ class JsonParserTests(TestCase):
             books.book[1],
         )
 
+    def test_parser_with_unknown_class(self):
+        path = fixtures_dir.joinpath("books/books.json")
+        books = self.parser.from_path(path)
+        self.assertIsInstance(books, Books)
+        self.assertEqual(2, len(books.book))
+
+        with self.assertRaises(ParserError) as cm:
+            self.parser.from_string('{"please": 1, "dont": 1, "exists": 2}')
+
+        self.assertEqual(
+            "No class found matching the document keys(['please', 'dont', 'exists'])",
+            str(cm.exception),
+        )
+
     def test_bind_value_with_attributes_var(self):
         var = XmlAttributes(name="a", qname="a")
         value = {"a": 1}
diff --git a/tests/formats/dataclass/parsers/test_nodes.py b/tests/formats/dataclass/parsers/test_nodes.py
index d44ba6d7..2d75bbdf 100644
--- a/tests/formats/dataclass/parsers/test_nodes.py
+++ b/tests/formats/dataclass/parsers/test_nodes.py
@@ -653,25 +653,26 @@ class SKipNodeTests(TestCase):
 
 
 class NodeParserTests(TestCase):
+    def setUp(self):
+        self.parser = NodeParser()
+
     def test_parse(self):
         @dataclass
         class TestHandler(XmlHandler):
             def parse(self, source: Any) -> Any:
                 return Books()
 
-        parser = NodeParser(handler=TestHandler)
-        result = parser.parse([], Books)
-        self.assertEqual(Books(), result)
+        self.parser.handler = TestHandler
+        self.assertEqual(Books(), self.parser.parse([], Books))
 
     def test_parse_when_result_type_is_wrong(self):
+        parser = self.parser
         with self.assertRaises(ParserError) as cm:
-            parser = NodeParser()
             parser.parse([], Books)
 
         self.assertEqual("Failed to create target class `Books`", str(cm.exception))
 
     def test_start(self):
-        parser = NodeParser()
         queue = []
         objects = []
 
@@ -679,28 +680,49 @@ class NodeParserTests(TestCase):
         ns_map = {"a": "b"}
         expected_node = ElementNode(
             position=0,
-            context=parser.context,
-            meta=parser.context.build(Books),
-            config=parser.config,
+            context=self.parser.context,
+            meta=self.parser.context.build(Books),
+            config=self.parser.config,
             attrs=attrs,
             ns_map=ns_map,
         )
-        parser.start(Books, queue, objects, "{urn:books}books", attrs, ns_map)
+        self.parser.start(Books, queue, objects, "{urn:books}books", attrs, ns_map)
         self.assertEqual(1, len(queue))
         self.assertEqual(expected_node, queue[0])
 
+        expected_node = replace(
+            expected_node, meta=self.parser.context.build(BookForm), attrs={}, ns_map={}
+        )
+        self.parser.start(Books, queue, objects, "book", {}, {})
+
+        self.assertEqual(2, len(queue))
+        self.assertEqual(expected_node, queue[-1])
+
+    def test_start_with_undefined_class(self):
+        parser = self.parser
+        queue = []
+        objects = []
+
+        attrs = {"k": "v"}
+        ns_map = {"a": "b"}
         expected_node = ElementNode(
             position=0,
             context=parser.context,
-            meta=parser.context.build(BookForm),
+            meta=parser.context.build(Books),
             config=parser.config,
-            attrs={},
-            ns_map={},
+            attrs=attrs,
+            ns_map=ns_map,
         )
-        parser.start(Books, queue, objects, "book", {}, {})
+        parser.start(None, queue, objects, "{urn:books}books", attrs, ns_map)
+        self.assertEqual(1, len(queue))
+        self.assertEqual(expected_node, queue[0])
 
-        self.assertEqual(2, len(queue))
-        self.assertEqual(expected_node, queue[-1])
+        with self.assertRaises(ParserError) as cm:
+            parser.start(None, [], [], "{unknown}hopefully", [], {})
+
+        self.assertEqual(
+            "No class found matching root: {unknown}hopefully", str(cm.exception)
+        )
 
     def test_start_with_derived_class(self):
         a = make_dataclass("a", fields=[])
diff --git a/tests/formats/dataclass/test_context.py b/tests/formats/dataclass/test_context.py
index 2b901ab1..0eea6393 100644
--- a/tests/formats/dataclass/test_context.py
+++ b/tests/formats/dataclass/test_context.py
@@ -1,6 +1,7 @@
 import sys
 from dataclasses import dataclass
 from dataclasses import field
+from dataclasses import fields
 from dataclasses import make_dataclass
 from dataclasses import replace
 from typing import Generator
@@ -96,6 +97,11 @@ class XmlContextTests(TestCase):
         self.ctx.xsi_cache["{urn:books}BookForm"].append(BooksForm)
         self.assertEqual(BooksForm, self.ctx.find_type("{urn:books}BookForm"))
 
+    def test_find_type_by_fields(self):
+        field_names = {f.name for f in fields(BookForm)}
+        self.assertEqual(BookForm, self.ctx.find_type_by_fields(field_names))
+        self.assertIsNone(self.ctx.find_type_by_fields({"please", "dont", "exist"}))
+
     def test_find_subclass(self):
         a = make_dataclass("A", fields=[])
         b = make_dataclass("B", fields=[], bases=(a,))