Repository: tefra/xsdata
Pull request #486 (2021-05-08 21:25:16)

Reduce context size
Trying to squeeze what's possible for the binding metadata, save a bit on storage and attribute access

codecov[bot]: # [Codecov](https://codecov.io/gh/tefra/xsdata/pull/486?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) Report
> Merging [#486](https://codecov.io/gh/tefra/xsdata/pull/486?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) (1082ba5) into [master](https://codecov.io/gh/tefra/xsdata/commit/ebd89136fb3d3969a0ec671c5e5a1f77c9cf7b06?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) (ebd8913) will **decrease** coverage by `0.08%`.
> The diff coverage is `95.55%`.

[![Impacted file tree graph](https://codecov.io/gh/tefra/xsdata/pull/486/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/486?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     #486      +/-   ##
===========================================
- Coverage   100.00%   99.91%   -0.09%     
===========================================
  Files           84       84              
  Lines         7350     7403      +53     
  Branches      1321     1334      +13     
===========================================
+ Hits          7350     7397      +47     
- Misses           0        3       +3     
- Partials         0        3       +3     
```


| [Impacted Files](https://codecov.io/gh/tefra/xsdata/pull/486?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/serializers/json.py](https://codecov.io/gh/tefra/xsdata/pull/486/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL3NlcmlhbGl6ZXJzL2pzb24ucHk=) | `100.00% <ø> (ø)` | |
| [xsdata/formats/dataclass/models/elements.py](https://codecov.io/gh/tefra/xsdata/pull/486/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL21vZGVscy9lbGVtZW50cy5weQ==) | `97.91% <95.12%> (-2.09%)` | :arrow_down: |
| [xsdata/utils/testing.py](https://codecov.io/gh/tefra/xsdata/pull/486/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL3V0aWxzL3Rlc3RpbmcucHk=) | `98.88% <95.55%> (-1.12%)` | :arrow_down: |
| [xsdata/formats/dataclass/models/builders.py](https://codecov.io/gh/tefra/xsdata/pull/486/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL21vZGVscy9idWlsZGVycy5weQ==) | `100.00% <100.00%> (ø)` | |
| [xsdata/formats/dataclass/parsers/json.py](https://codecov.io/gh/tefra/xsdata/pull/486/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL3BhcnNlcnMvanNvbi5weQ==) | `100.00% <100.00%> (ø)` | |
| [xsdata/formats/dataclass/parsers/tree.py](https://codecov.io/gh/tefra/xsdata/pull/486/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL3BhcnNlcnMvdHJlZS5weQ==) | `100.00% <100.00%> (ø)` | |
| [xsdata/utils/constants.py](https://codecov.io/gh/tefra/xsdata/pull/486/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL3V0aWxzL2NvbnN0YW50cy5weQ==) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tefra/xsdata/pull/486?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/486?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou). Last update [ebd8913...1082ba5](https://codecov.io/gh/tefra/xsdata/pull/486?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=486&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=486&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=CODE_SMELL) [2 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=486&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=486) 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=486&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=CODE_SMELL) [2 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=486&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=486) 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=486&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=CODE_SMELL) [2 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=486&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=486) 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=486&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=CODE_SMELL) [2 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=486&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=486) 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=486&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=486&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=486&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=486&resolved=false&types=CODE_SMELL) [2 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=486&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=486) 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=486&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=486&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/xsdata/formats/dataclass/models/builders.py b/xsdata/formats/dataclass/models/builders.py
index 57b945a2..abe8c982 100644
--- a/xsdata/formats/dataclass/models/builders.py
+++ b/xsdata/formats/dataclass/models/builders.py
@@ -66,7 +66,7 @@ class XmlMetaBuilder:
         )
 
         attributes = {}
-        elements = defaultdict(list)
+        elements: Dict[str, List[XmlVar]] = defaultdict(list)
         choices = []
         any_attributes = []
         wildcards = []
@@ -193,7 +193,7 @@ class XmlVarBuilder:
     ) -> XmlVar:
         """Build the binding metadata for a dataclass field."""
         tokens = metadata.get("tokens", False)
-        xml_type = metadata.get("type")
+        xml_type = metadata.get("type", self.default_xml_type)
         local_name = metadata.get("name")
         namespace = metadata.get("namespace")
         choices = metadata.get("choices", EMPTY_SEQUENCE)
@@ -203,8 +203,6 @@ class XmlVarBuilder:
         sequential = metadata.get("sequential", False)
 
         origin, sub_origin, types = self.analyze_types(type_hint, globalns)
-        is_class = self.is_class(types)
-        xml_type = self.build_xml_type(xml_type, is_class)
 
         if not self.is_valid(xml_type, origin, sub_origin, types, tokens, init):
             raise XmlContextError(
@@ -236,7 +234,6 @@ class XmlVarBuilder:
             tokens=tokens,
             any_type=any_type,
             nillable=nillable,
-            dataclass=is_class,
             sequential=sequential,
             list_element=element_list,
             default=default_value,
@@ -244,21 +241,10 @@ class XmlVarBuilder:
             elements=elements,
             wildcards=wildcards,
             namespaces=namespaces,
+            xml_type=xml_type,
+            derived=False,
         )
 
-        if xml_type == XmlType.ELEMENT:
-            var.is_element = True
-        elif xml_type == XmlType.ELEMENTS:
-            var.is_elements = True
-        elif xml_type == XmlType.ATTRIBUTE:
-            var.is_attribute = True
-        elif xml_type == XmlType.ATTRIBUTES:
-            var.is_attributes = True
-        elif xml_type == XmlType.WILDCARD:
-            var.is_wildcard = True
-        else:
-            var.is_text = True
-
         return var
 
     def build_choices(
@@ -304,19 +290,6 @@ class XmlVarBuilder:
 
         return local_name
 
-    def build_xml_type(self, xml_type: Optional[str], is_class: bool) -> str:
-        """Build the xml type if it's not set based on the default type for the
-        current class and the field complex/simple type."""
-        if not xml_type:
-            xml_type = XmlType.ELEMENT if is_class else self.default_xml_type
-
-        return xml_type
-
-    @classmethod
-    def is_class(cls, types: Sequence[Type]) -> bool:
-        """Return whether at least one of the types is a dataclass."""
-        return any(is_dataclass(clazz) for clazz in types)
-
     def resolve_namespaces(
         self,
         xml_type: Optional[str],
diff --git a/xsdata/formats/dataclass/models/elements.py b/xsdata/formats/dataclass/models/elements.py
index 2dd43cd5..c4acf9f2 100644
--- a/xsdata/formats/dataclass/models/elements.py
+++ b/xsdata/formats/dataclass/models/elements.py
@@ -1,97 +1,193 @@
 import operator
-from dataclasses import dataclass
-from dataclasses import field
 from dataclasses import is_dataclass
 from typing import Any
 from typing import Dict
 from typing import Iterator
 from typing import List
+from typing import Mapping
 from typing import Optional
 from typing import Sequence
-from typing import Tuple
 from typing import Type
 
 from xsdata.models.enums import NamespaceType
 from xsdata.utils import collections
 from xsdata.utils.constants import EMPTY_SEQUENCE
+from xsdata.utils.namespaces import local_name
 from xsdata.utils.namespaces import split_qname
+from xsdata.utils.namespaces import target_uri
 
 NoneType = type(None)
 
 
-@dataclass
-class XmlVar:
-    """
-    Dataclass field binding metadata.
-
-    :param name: Field name
-    :param qname: Qualified name
-    :param init:  Include field in the constructor
-    :param mixed:  Field supports mixed content type values
-    :param tokens: Field is derived from xs:list
-    :param format: Value format information
-    :param derived: Wrap parsed values with
-        :class:`~xsdata.formats.dataclass.models.generics.DerivedElement`
-    :param any_type: Field supports dynamic value types
-    :param nillable: Field supports nillable content
-    :param dataclass: Field value is bound to a dataclass
-    :param sequential: Render values in sequential mode
-    :param list_element: Field is a list of elements
-    :param default: Field default value or factory
-    :param is_text: Field is derived from xs:simpleType
-    :param is_element: Field is derived from xs:element
-    :param is_elements: Field is derived from xs:choice
-    :param is_wildcard: Field is derived from xs:anyType
-    :param is_attribute: Field is derived from xs:attribute
-    :param is_attributes: Field is derived from xs:attributes
-    :param index: Field ordering
-    :param types: List of all the supported data types
-    :param namespaces: List of the supported namespaces
-    :param elements: Mapping of qname-repeatable elements
-    :param wildcards: List of repeatable wildcards
-    :ivar namespace_matches: Matching cache for the repeatable wildcards
-    """
-
-    name: str
-    qname: str
-    init: bool = True
-    mixed: bool = False
-    tokens: bool = False
-    format: Optional[str] = None
-    derived: bool = False
-    any_type: bool = False
-    nillable: bool = False
-    dataclass: bool = False
-    sequential: bool = False
-    list_element: bool = False
-    default: Any = None
-    is_text: bool = False
-    is_element: bool = False
-    is_elements: bool = False
-    is_wildcard: bool = False
-    is_attribute: bool = False
-    is_attributes: bool = False
-    index: int = field(default_factory=int)
-    types: Tuple[Type, ...] = field(default_factory=tuple)
-    namespaces: Tuple[str, ...] = field(default_factory=tuple)
-    elements: Dict[str, "XmlVar"] = field(default_factory=dict)
-    wildcards: List["XmlVar"] = field(default_factory=list)
-    namespace_matches: Optional[Dict[str, bool]] = field(init=False, default=None)
+class XmlType:
+    """Xml node types."""
 
-    @property
-    def lname(self) -> str:
-        """Local name."""
-        _, name = split_qname(self.qname)
-        return name
+    TEXT = "Text"
+    ELEMENT = "Element"
+    ELEMENTS = "Elements"
+    WILDCARD = "Wildcard"
+    ATTRIBUTE = "Attribute"
+    ATTRIBUTES = "Attributes"
 
-    @property
-    def clazz(self) -> Optional[Type]:
-        """Return the first type if field is bound to a dataclass."""
-        return self.types[0] if self.dataclass else None
+    @classmethod
+    def all(cls):
+        yield XmlType.TEXT
+        yield XmlType.ELEMENT
+        yield XmlType.ELEMENTS
+        yield XmlType.WILDCARD
+        yield XmlType.ATTRIBUTE
+        yield XmlType.ATTRIBUTES
+
+
+class MetaMixin:
+    def __eq__(self, other: Any) -> bool:
+        if isinstance(other, self.__class__):
+            return tuple(self) == tuple(other)
+
+        return NotImplemented
+
+    def __iter__(self) -> Iterator:
+        for field_name in self.__slots__:
+            yield getattr(self, field_name)
+
+
+class XmlVar(MetaMixin):
+    """Class field binding metadata."""
+
+    __slots__ = (
+        "name",
+        "qname",
+        "init",
+        "mixed",
+        "tokens",
+        "format",
+        "derived",
+        "any_type",
+        "nillable",
+        "sequential",
+        "list_element",
+        "default",
+        "is_text",
+        "is_element",
+        "is_elements",
+        "is_wildcard",
+        "is_attribute",
+        "is_attributes",
+        "index",
+        "types",
+        "namespaces",
+        "elements",
+        "wildcards",
+        "namespace_matches",
+        "clazz",
+        "is_clazz_union",
+        "local_name",
+    )
+
+    def __init__(
+        self,
+        *args: Any,
+        index: int,
+        name: str,
+        qname: str,
+        types: Sequence[Type],
+        init: bool,
+        mixed: bool,
+        tokens: bool,
+        format: Optional[str],
+        derived: bool,
+        any_type: bool,
+        nillable: bool,
+        sequential: bool,
+        list_element: bool,
+        default: Any,
+        xml_type: str,
+        namespaces: Sequence[str],
+        elements: Mapping[str, "XmlVar"],
+        wildcards: Sequence["XmlVar"],
+    ):
+        """
+        :param index: Field ordering
+        :param name: Field name
+        :param qname: Qualified name
+        :param types: List of all the supported data types
+        :param init:  Include field in the constructor
+        :param mixed:  Field supports mixed content type values
+        :param tokens: Field is derived from xs:list
+        :param format: Value format information
+        :param derived: Wrap parsed values with a generic type
+        :param any_type: Field supports dynamic value types
+        :param nillable: Field supports nillable content
+        :param sequential: Render values in sequential mode
+        :param list_element: Field is a list of elements
+        :param default: Field default value or factory
+        :param xml_Type: Field xml type
+        :param namespaces: List of the supported namespaces
+        :param elements: Mapping of qname-repeatable elements
+        :param wildcards: List of repeatable wildcards
+        """
+
+        self.index = index
+        self.name = name
+        self.qname = qname
+        self.types = types
+        self.init = init
+        self.mixed = mixed
+        self.tokens = tokens
+        self.format = format
+        self.derived = derived
+        self.any_type = any_type
+        self.nillable = nillable
+        self.sequential = sequential
+        self.list_element = list_element
+        self.default = default
+        self.namespaces = namespaces
+        self.elements = elements
+        self.wildcards = wildcards
+
+        self.namespace_matches: Optional[Dict[str, bool]] = None
+
+        self.clazz = collections.first(tp for tp in types if is_dataclass(tp))
+        self.is_clazz_union = self.clazz and len(types) > 1
+        self.local_name = local_name(qname)
+
+        self.is_text = False
+        self.is_element = False
+        self.is_elements = False
+        self.is_wildcard = False
+        self.is_attribute = False
+        self.is_attributes = False
+
+        if xml_type == XmlType.ELEMENT or self.clazz:
+            self.is_element = True
+        elif xml_type == XmlType.ELEMENTS:
+            self.is_elements = True
+        elif xml_type == XmlType.ATTRIBUTE:
+            self.is_attribute = True
+        elif xml_type == XmlType.ATTRIBUTES:
+            self.is_attributes = True
+        elif xml_type == XmlType.WILDCARD:
+            self.is_wildcard = True
+        else:
+            self.is_text = True
 
-    @property
-    def is_clazz_union(self) -> bool:
-        return self.dataclass and len(self.types) > 1
+    def get_xml_type(self) -> str:
+        if self.is_wildcard:
+            return XmlType.WILDCARD
+
+        if self.is_attribute:
+            return XmlType.ATTRIBUTE
+
+        if self.is_attributes:
+            return XmlType.ATTRIBUTES
+
+        if self.is_element:
+            return XmlType.ELEMENT
+
+        if self.is_elements:
+            return XmlType.ELEMENTS
+
+        return XmlType.TEXT
 
     def find_choice(self, qname: str) -> Optional["XmlVar"]:
         """Match and return a choice field by its qualified name."""
@@ -156,64 +252,110 @@ class XmlVar:
         if qname == "*":
             return True
 
-        namespace, tag = split_qname(qname)
-        if not self.namespaces and namespace is None:
+        uri = target_uri(qname)
+        if not self.namespaces and uri is None:
             return True
 
         for check in self.namespaces:
             if (
-                (not check and namespace is None)
-                or check == namespace
+                (not check and uri is None)
+                or check == uri
                 or check == NamespaceType.ANY_NS
-                or (check and check[0] == "!" and check[1:] != namespace)
+                or (check and check[0] == "!" and check[1:] != uri)
             ):
                 return True
 
         return False
 
+    def __repr__(self) -> str:
+        params = (
+            f"index={self.index}, "
+            f"name={self.name}, "
+            f"qname={self.qname}, "
+            f"types={self.types}, "
+            f"init={self.init}, "
+            f"mixed={self.mixed}, "
+            f"tokens={self.tokens}, "
+            f"format={self.format}, "
+            f"derived={self.derived}, "
+            f"any_type={self.any_type}, "
+            f"nillable={self.nillable}, "
+            f"sequential={self.sequential}, "
+            f"list_element={self.list_element}, "
+            f"default={self.default}, "
+            f"xml_type={self.get_xml_type()}, "
+            f"namespaces={self.namespaces}, "
+            f"elements={self.elements}, "
+            f"wildcards={self.wildcards}"
+        )
+        return f"{self.__class__.__name__}({params})"
+
 
 get_index = operator.attrgetter("index")
 
 
-@dataclass
-class XmlMeta:
-    """
-    Dataclass binding metadata.
-
-    :param clazz: The dataclass type
-    :param qname: The namespace qualified name.
-    :param source_qname: The source namespace qualified name.
-    :param nillable: Specifies whether an explicit empty value can be assigned.
-    :param mixed_content: Has a wildcard with mixed flag enabled
-    :param text: Text var
-    :param choices: List of compound vars
-    :param elements: Mapping of qname-element vars
-    :param wildcards: List of wildcard vars
-    :param attributes: Mapping of qname-attribute vars
-    :param any_attributes: List of wildcard attributes vars
-    """
-
-    clazz: Type
-    qname: str
-    source_qname: str
-    nillable: bool
-    mixed_content: bool = field(default=False)
-    text: Optional[XmlVar] = field(default=None)
-    choices: List[XmlVar] = field(default_factory=list)
-    elements: Dict[str, List[XmlVar]] = field(default_factory=dict)
-    wildcards: List[XmlVar] = field(default_factory=list)
-    attributes: Dict[str, XmlVar] = field(default_factory=dict)
-    any_attributes: List[XmlVar] = field(default_factory=list)
-
-    def __post_init__(self):
+class XmlMeta(MetaMixin):
+    """Class binding metadata."""
+
+    __slots__ = (
+        "clazz",
+        "qname",
+        "source_qname",
+        "nillable",
+        "text",
+        "choices",
+        "elements",
+        "wildcards",
+        "attributes",
+        "any_attributes",
+        "mixed_content",
+    )
+
+    def __init__(
+        self,
+        *args: Any,
+        clazz: Type,
+        qname: str,
+        source_qname: str,
+        nillable: bool,
+        text: Optional[XmlVar],
+        choices: Sequence[XmlVar],
+        elements: Mapping[str, Sequence[XmlVar]],
+        wildcards: Sequence[XmlVar],
+        attributes: Mapping[str, XmlVar],
+        any_attributes: Sequence[XmlVar],
+    ):
+
+        """
+        :param clazz: The dataclass type
+        :param qname: The namespace qualified name.
+        :param source_qname: The source namespace qualified name.
+        :param nillable: Specifies whether an explicit empty value can be assigned.
+        :param mixed_content: Has a wildcard with mixed flag enabled
+        :param text: Text var
+        :param choices: List of compound vars
+        :param elements: Mapping of qname-element vars
+        :param wildcards: List of wildcard vars
+        :param attributes: Mapping of qname-attribute vars
+        :param any_attributes: List of wildcard attributes vars
+        """
+
+        self.clazz = clazz
+        self.qname = qname
+        self.source_qname = source_qname
+        self.nillable = nillable
+        self.text = text
+        self.choices = choices
+        self.elements = elements
+        self.wildcards = wildcards
+        self.attributes = attributes
+        self.any_attributes = any_attributes
         self.mixed_content = any(wildcard.mixed for wildcard in self.wildcards)
 
     def get_element_vars(self) -> List[XmlVar]:
-        result = self.wildcards + self.choices
-
-        for elements in self.elements.values():
-            result.extend(elements)
-
+        result = list(
+            collections.concat(self.wildcards, self.choices, *self.elements.values())
+        )
         if self.text:
             result.append(self.text)
 
@@ -224,11 +366,15 @@ class XmlMeta:
         return sorted(result, key=get_index)
 
     def get_all_vars(self) -> List[XmlVar]:
-        result = self.wildcards + self.choices + self.any_attributes
-        result.extend(self.attributes.values())
-        for elements in self.elements.values():
-            result.extend(elements)
-
+        result = list(
+            collections.concat(
+                self.wildcards,
+                self.choices,
+                self.any_attributes,
+                self.attributes.values(),
+                *self.elements.values(),
+            )
+        )
         if self.text:
             result.append(self.text)
 
@@ -270,19 +416,23 @@ class XmlMeta:
         if chd:
             yield chd
 
-
-class XmlType:
-    """Xml node types."""
-
-    TEXT = "Text"
-    ELEMENT = "Element"
-    ELEMENTS = "Elements"
-    WILDCARD = "Wildcard"
-    ATTRIBUTE = "Attribute"
-    ATTRIBUTES = "Attributes"
-
-
-def find_by_namespace(xml_vars: List[XmlVar], qname: str) -> Optional[XmlVar]:
+    def __repr__(self) -> str:
+        params = (
+            f"clazz={self.clazz}, "
+            f"qname={self.qname}, "
+            f"source_qname={self.source_qname}, "
+            f"nillable={self.nillable}, "
+            f"text={self.text}, "
+            f"choices={self.choices}, "
+            f"elements={self.elements}, "
+            f"wildcards={self.wildcards}, "
+            f"attributes={self.attributes}, "
+            f"any_attributes={self.any_attributes}"
+        )
+        return f"{self.__class__.__name__}({params})"
+
+
+def find_by_namespace(xml_vars: Sequence[XmlVar], qname: str) -> Optional[XmlVar]:
     for xml_var in xml_vars:
         if xml_var.match_namespace(qname):
             return xml_var
diff --git a/xsdata/formats/dataclass/parsers/json.py b/xsdata/formats/dataclass/parsers/json.py
index 269fb227..006b94ec 100644
--- a/xsdata/formats/dataclass/parsers/json.py
+++ b/xsdata/formats/dataclass/parsers/json.py
@@ -122,7 +122,7 @@ class JsonParser(AbstractParser):
         """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.lname)
+            value = data.get(var.local_name)
 
             if value is None or not var.init:
                 continue
@@ -204,7 +204,7 @@ class JsonParser(AbstractParser):
         # 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.dataclass or choice.tokens != is_list:
+            if choice.clazz or choice.tokens != is_list:
                 continue
 
             with warnings.catch_warnings(record=True) as w:
@@ -238,7 +238,7 @@ class JsonParser(AbstractParser):
         for choice in var.elements.values():
             if choice.clazz:
                 meta = self.context.build(choice.clazz)
-                attrs = {var.lname for var in meta.get_all_vars()}
+                attrs = {var.local_name for var in meta.get_all_vars()}
                 if attrs == keys:
                     return self.bind_value(choice, value)
 
diff --git a/xsdata/formats/dataclass/parsers/tree.py b/xsdata/formats/dataclass/parsers/tree.py
index 61c587e6..e5a5b02e 100644
--- a/xsdata/formats/dataclass/parsers/tree.py
+++ b/xsdata/formats/dataclass/parsers/tree.py
@@ -5,6 +5,7 @@ from typing import List
 from typing import Optional
 from typing import Type
 
+from xsdata.formats.dataclass.models.elements import XmlType
 from xsdata.formats.dataclass.models.elements import XmlVar
 from xsdata.formats.dataclass.parsers.handlers import default_handler
 from xsdata.formats.dataclass.parsers.mixins import XmlHandler
@@ -37,10 +38,26 @@ class TreeParser(NodeParser):
             item = queue[-1]
             child = item.child(qname, attrs, ns_map, len(objects))
         except IndexError:
-            child = WildcardNode(
-                var=XmlVar(name=qname, qname=qname, is_wildcard=True),
-                attrs=attrs,
-                ns_map=ns_map,
-                position=0,
+            var = XmlVar(
+                name=qname,
+                qname=qname,
+                xml_type=XmlType.WILDCARD,
+                index=0,
+                types=(object,),
+                init=True,
+                mixed=False,
+                tokens=False,
+                format=None,
+                derived=False,
+                any_type=False,
+                nillable=False,
+                sequential=False,
+                list_element=False,
+                default=None,
+                namespaces=(),
+                elements={},
+                wildcards=(),
             )
+
+            child = WildcardNode(var=var, attrs=attrs, ns_map=ns_map, position=0)
         queue.append(child)
diff --git a/xsdata/formats/dataclass/serializers/json.py b/xsdata/formats/dataclass/serializers/json.py
index 0e53176e..cd516ada 100644
--- a/xsdata/formats/dataclass/serializers/json.py
+++ b/xsdata/formats/dataclass/serializers/json.py
@@ -66,7 +66,7 @@ class JsonSerializer(AbstractSerializer):
 
             return self.dict_factory(
                 [
-                    (var.lname, self.convert(getattr(obj, var.name), var))
+                    (var.local_name, self.convert(getattr(obj, var.name), var))
                     for var in self.context.build(obj.__class__).get_all_vars()
                 ]
             )
diff --git a/xsdata/utils/constants.py b/xsdata/utils/constants.py
index 758484b2..e3bbaeb9 100644
--- a/xsdata/utils/constants.py
+++ b/xsdata/utils/constants.py
@@ -1,9 +1,11 @@
 from typing import Any
 from typing import Dict
 from typing import Sequence
+from typing import Tuple
 
 EMPTY_MAP: Dict = {}
 EMPTY_SEQUENCE: Sequence = []
+EMPTY_TUPLE: Tuple = ()
 
 
 def return_true(*_: Any) -> bool:


Test patch:
diff --git a/tests/formats/dataclass/models/test_builders.py b/tests/formats/dataclass/models/test_builders.py
index dfb29587..3e6005b4 100644
--- a/tests/formats/dataclass/models/test_builders.py
+++ b/tests/formats/dataclass/models/test_builders.py
@@ -20,28 +20,29 @@ from tests.fixtures.defxmlschema.chapter13 import ItemsType
 from xsdata.exceptions import XmlContextError
 from xsdata.formats.dataclass.models.builders import XmlMetaBuilder
 from xsdata.formats.dataclass.models.builders import XmlVarBuilder
-from xsdata.formats.dataclass.models.elements import XmlMeta
 from xsdata.formats.dataclass.models.elements import XmlType
-from xsdata.formats.dataclass.models.elements import XmlVar
 from xsdata.models.datatype import XmlDate
 from xsdata.utils import text
 from xsdata.utils.constants import return_input
 from xsdata.utils.constants import return_true
 from xsdata.utils.namespaces import build_qname
+from xsdata.utils.testing import FactoryTestCase
+from xsdata.utils.testing import XmlMetaFactory
+from xsdata.utils.testing import XmlVarFactory
 
 
-class XmlMetaBuilderTests(TestCase):
+class XmlMetaBuilderTests(FactoryTestCase):
     @mock.patch.object(XmlMetaBuilder, "build_vars")
     def test_build(self, mock_build_vars):
-        var = XmlVar(is_element=True, name="foo", qname="{foo}bar", types=(int,))
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENT, name="foo", qname="{foo}bar", types=(int,)
+        )
         mock_build_vars.return_value = [var]
 
         result = XmlMetaBuilder.build(ItemsType, None, return_input, return_input)
-        expected = XmlMeta(
+        expected = XmlMetaFactory.create(
             clazz=ItemsType,
             qname="ItemsType",
-            source_qname="ItemsType",
-            nillable=False,
             elements={var.qname: [var]},
         )
 
@@ -110,51 +111,53 @@ class XmlMetaBuilderTests(TestCase):
         self.assertIsInstance(result, Iterator)
 
         expected = [
-            XmlVar(
-                is_element=True,
+            XmlVarFactory.create(
+                xml_type=XmlType.ELEMENT,
                 index=0,
                 name="author",
                 qname="Author",
                 types=(str,),
             ),
-            XmlVar(
-                is_element=True,
+            XmlVarFactory.create(
+                xml_type=XmlType.ELEMENT,
                 index=1,
                 name="title",
                 qname="Title",
                 types=(str,),
             ),
-            XmlVar(
-                is_element=True,
+            XmlVarFactory.create(
+                xml_type=XmlType.ELEMENT,
                 index=2,
                 name="genre",
                 qname="Genre",
                 types=(str,),
             ),
-            XmlVar(
-                is_element=True,
+            XmlVarFactory.create(
+                xml_type=XmlType.ELEMENT,
                 index=3,
                 name="price",
                 qname="Price",
                 types=(float,),
             ),
-            XmlVar(
-                is_element=True,
+            XmlVarFactory.create(
+                xml_type=XmlType.ELEMENT,
                 index=4,
                 name="pub_date",
                 qname="PubDate",
                 types=(XmlDate,),
             ),
-            XmlVar(
-                is_element=True,
+            XmlVarFactory.create(
+                xml_type=XmlType.ELEMENT,
                 index=5,
                 name="review",
                 qname="Review",
                 types=(str,),
             ),
-            XmlVar(is_attribute=True, index=6, name="id", qname="ID", types=(str,)),
-            XmlVar(
-                is_attribute=True,
+            XmlVarFactory.create(
+                xml_type=XmlType.ATTRIBUTE, index=6, name="id", qname="ID", types=(str,)
+            ),
+            XmlVarFactory.create(
+                xml_type=XmlType.ATTRIBUTE,
                 index=7,
                 name="lang",
                 qname="LANG",
@@ -167,7 +170,6 @@ class XmlMetaBuilderTests(TestCase):
         result = list(result)
         self.assertEqual(expected, result)
         for var in result:
-            self.assertFalse(var.dataclass)
             self.assertIsNone(var.clazz)
 
     def test_default_xml_type(self):
@@ -229,29 +231,28 @@ class XmlVarBuilderTests(TestCase):
             list,
             globalns,
         )
-        expected = XmlVar(
+        expected = XmlVarFactory.create(
             index=66,
-            is_elements=True,
+            xml_type=XmlType.ELEMENTS,
             name="compound",
             qname="compound",
             list_element=True,
             any_type=True,
             default=list,
             elements={
-                "{foo}node": XmlVar(
+                "{foo}node": XmlVarFactory.create(
                     index=0,
-                    is_element=True,
+                    xml_type=XmlType.ELEMENT,
                     name="compound",
                     qname="{foo}node",
-                    dataclass=True,
                     list_element=True,
                     types=(CompoundFieldExample,),
                     namespaces=("foo",),
                     derived=False,
                 ),
-                "{bar}x": XmlVar(
+                "{bar}x": XmlVarFactory.create(
                     index=1,
-                    is_element=True,
+                    xml_type=XmlType.ELEMENT,
                     name="compound",
                     qname="{bar}x",
                     tokens=True,
@@ -262,9 +263,9 @@ class XmlVarBuilderTests(TestCase):
                     default=return_true,
                     format="Nope",
                 ),
-                "{bar}y": XmlVar(
+                "{bar}y": XmlVarFactory.create(
                     index=2,
-                    is_element=True,
+                    xml_type=XmlType.ELEMENT,
                     name="compound",
                     qname="{bar}y",
                     nillable=True,
@@ -273,9 +274,9 @@ class XmlVarBuilderTests(TestCase):
                     namespaces=("bar",),
                     derived=False,
                 ),
-                "{bar}z": XmlVar(
+                "{bar}z": XmlVarFactory.create(
                     index=3,
-                    is_element=True,
+                    xml_type=XmlType.ELEMENT,
                     name="compound",
                     qname="{bar}z",
                     nillable=False,
@@ -284,9 +285,9 @@ class XmlVarBuilderTests(TestCase):
                     namespaces=("bar",),
                     derived=True,
                 ),
-                "{bar}o": XmlVar(
+                "{bar}o": XmlVarFactory.create(
                     index=4,
-                    is_element=True,
+                    xml_type=XmlType.ELEMENT,
                     name="compound",
                     qname="{bar}o",
                     nillable=False,
@@ -296,9 +297,9 @@ class XmlVarBuilderTests(TestCase):
                     derived=True,
                     any_type=True,
                 ),
-                "{bar}p": XmlVar(
+                "{bar}p": XmlVarFactory.create(
                     index=5,
-                    is_element=True,
+                    xml_type=XmlType.ELEMENT,
                     name="compound",
                     qname="{bar}p",
                     types=(float,),
@@ -308,9 +309,9 @@ class XmlVarBuilderTests(TestCase):
                 ),
             },
             wildcards=[
-                XmlVar(
+                XmlVarFactory.create(
                     index=6,
-                    is_wildcard=True,
+                    xml_type=XmlType.WILDCARD,
                     name="compound",
                     qname="{http://www.w3.org/1999/xhtml}any",
                     types=(object,),
@@ -385,12 +386,6 @@ class XmlVarBuilderTests(TestCase):
             str(cm.exception),
         )
 
-    def test_build_xml_type(self):
-        self.builder.default_xml_type = XmlType.WILDCARD
-        self.assertEqual(XmlType.WILDCARD, self.builder.build_xml_type(None, False))
-        self.assertEqual(XmlType.ELEMENT, self.builder.build_xml_type("", True))
-        self.assertEqual(XmlType.TEXT, self.builder.build_xml_type(XmlType.TEXT, True))
-
     def test_is_valid(self):
         # Attributes need origin dict
         self.assertFalse(
diff --git a/tests/formats/dataclass/parsers/test_json.py b/tests/formats/dataclass/parsers/test_json.py
index 324c1939..2045854d 100644
--- a/tests/formats/dataclass/parsers/test_json.py
+++ b/tests/formats/dataclass/parsers/test_json.py
@@ -9,15 +9,17 @@ from tests import fixtures_dir
 from tests.fixtures.books import BookForm
 from tests.fixtures.books import Books
 from xsdata.exceptions import ParserError
-from xsdata.formats.dataclass.models.elements import XmlVar
+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(TestCase):
+class JsonParserTests(FactoryTestCase):
     def setUp(self) -> None:
         super().setUp()
 
@@ -126,7 +128,7 @@ class JsonParserTests(TestCase):
         self.assertEqual("Key `book` value is not iterable", str(cm.exception))
 
     def test_bind_value_with_attributes_var(self):
-        var = XmlVar(is_attributes=True, name="a", qname="a")
+        var = XmlVarFactory.create(xml_type=XmlType.ATTRIBUTES, name="a")
         value = {"a": 1}
         actual = self.parser.bind_value(var, value)
         self.assertEqual(value, actual)
@@ -137,12 +139,11 @@ class JsonParserTests(TestCase):
         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 = XmlVar(
-            is_element=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENT,
             name="union",
             qname="union",
             types=(a, b, c, int),
-            dataclass=True,
         )
 
         data = {"x": 1, "y": "foo", "z": "foo"}
@@ -152,39 +153,43 @@ class JsonParserTests(TestCase):
 
     def test_bind_type_union(self):
         a = make_dataclass("a", [("x", int), ("y", str)])
-        var = XmlVar(
-            is_element=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENT,
             name="union",
             qname="union",
             types=(a, int, float),
-            dataclass=True,
         )
 
         data = "1.1"
         self.assertEqual(1.1, self.parser.bind_value(var, data))
 
     def test_bind_choice_simple(self):
-        var = XmlVar(
-            is_elements=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENTS,
             qname="compound",
             name="compound",
             elements={
-                "int": XmlVar(is_element=True, qname="int", name="int", types=(int,)),
-                "tokens": XmlVar(
-                    is_element=True,
+                "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,
                 ),
-                "generic": XmlVar(
-                    is_element=True, qname="generic", name="generic", dataclass=True
-                ),
-                "float": XmlVar(
-                    is_element=True, qname="float", name="float", types=(float,)
+                "float": XmlVarFactory.create(
+                    xml_type=XmlType.ELEMENT,
+                    qname="float",
+                    name="float",
+                    types=(float,),
                 ),
-                "qname": XmlVar(
-                    is_element=True, qname="qname", name="qname", types=(QName,)
+                "qname": XmlVarFactory.create(
+                    xml_type=XmlType.ELEMENT,
+                    qname="qname",
+                    name="qname",
+                    types=(QName,),
                 ),
             },
         )
@@ -208,18 +213,26 @@ class JsonParserTests(TestCase):
         a = make_dataclass("a", [("x", int)])
         b = make_dataclass("b", [("x", int), ("y", str)])
 
-        var = XmlVar(
-            is_elements=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENTS,
             qname="compound",
             name="compound",
             elements={
-                "a": XmlVar(
-                    is_element=True, qname="a", name="a", types=(a,), dataclass=True
+                "a": XmlVarFactory.create(
+                    xml_type=XmlType.ELEMENT,
+                    qname="a",
+                    name="a",
+                    types=(a,),
                 ),
-                "b": XmlVar(
-                    is_element=True, qname="b", name="b", types=(b,), dataclass=True
+                "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,)
                 ),
-                "c": XmlVar(is_element=True, qname="c", name="c", types=(int,)),
             },
         )
 
@@ -235,13 +248,17 @@ class JsonParserTests(TestCase):
         )
 
     def test_bind_choice_generic_with_derived(self):
-        var = XmlVar(
-            is_elements=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENTS,
             qname="compound",
             name="compound",
             elements={
-                "a": XmlVar(is_element=True, name="a", qname="a", types=(int,)),
-                "b": XmlVar(is_element=True, name="b", qname="b", types=(float,)),
+                "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}
@@ -252,13 +269,17 @@ class JsonParserTests(TestCase):
         )
 
     def test_bind_choice_generic_with_wildcard(self):
-        var = XmlVar(
-            is_elements=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENTS,
             qname="compound",
             name="compound",
             elements={
-                "a": XmlVar(is_element=True, name="a", qname="a", types=(int,)),
-                "b": XmlVar(is_element=True, name="b", qname="b", types=(float,)),
+                "a": XmlVarFactory.create(
+                    xml_type=XmlType.ELEMENT, name="a", types=(int,)
+                ),
+                "b": XmlVarFactory.create(
+                    xml_type=XmlType.ELEMENT, name="b", types=(float,)
+                ),
             },
         )
 
@@ -268,7 +289,9 @@ class JsonParserTests(TestCase):
         )
 
     def test_bind_choice_generic_with_unknown_qname(self):
-        var = XmlVar(is_elements=True, qname="compound", name="compound")
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENTS, qname="compound", name="compound"
+        )
 
         with self.assertRaises(ParserError) as cm:
             self.parser.bind_choice({"qname": "foo", "text": 1}, var)
@@ -279,8 +302,8 @@ class JsonParserTests(TestCase):
         )
 
     def test_bind_wildcard_with_any_element(self):
-        var = XmlVar(
-            is_wildcard=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.WILDCARD,
             name="any_element",
             qname="any_element",
             types=(object,),
@@ -292,7 +315,7 @@ class JsonParserTests(TestCase):
         )
 
     def test_bind_wildcard_with_derived_element(self):
-        var = XmlVar(
+        var = XmlVarFactory.create(
             any_type=True,
             name="a",
             qname="a",
@@ -304,7 +327,7 @@ class JsonParserTests(TestCase):
         self.assertEqual(actual, self.parser.bind_value(var, data))
 
     def test_bind_wildcard_with_no_matching_value(self):
-        var = XmlVar(
+        var = XmlVarFactory.create(
             any_type=True,
             name="a",
             qname="a",
diff --git a/tests/formats/dataclass/parsers/test_nodes.py b/tests/formats/dataclass/parsers/test_nodes.py
index 5a20febc..97736719 100644
--- a/tests/formats/dataclass/parsers/test_nodes.py
+++ b/tests/formats/dataclass/parsers/test_nodes.py
@@ -1,3 +1,4 @@
+import copy
 from dataclasses import dataclass
 from dataclasses import field
 from dataclasses import make_dataclass
@@ -14,7 +15,7 @@ from xsdata.exceptions import ParserError
 from xsdata.exceptions import XmlContextError
 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.elements import XmlType
 from xsdata.formats.dataclass.models.generics import AnyElement
 from xsdata.formats.dataclass.models.generics import DerivedElement
 from xsdata.formats.dataclass.parsers.config import ParserConfig
@@ -31,6 +32,9 @@ from xsdata.models.enums import DataType
 from xsdata.models.enums import Namespace
 from xsdata.models.enums import QNames
 from xsdata.models.mixins import attribute
+from xsdata.utils.testing import FactoryTestCase
+from xsdata.utils.testing import XmlMetaFactory
+from xsdata.utils.testing import XmlVarFactory
 
 
 @dataclass
@@ -73,11 +77,11 @@ def add_content(x, *args):
     x["content"] = 3
 
 
-class ElementNodeTests(TestCase):
+class ElementNodeTests(FactoryTestCase):
     def setUp(self) -> None:
         super().setUp()
         self.context = XmlContext()
-        self.meta = XmlMeta(clazz=Foo, qname="foo", source_qname="foo", nillable=False)
+        self.meta = XmlMetaFactory.create(clazz=Foo, qname="foo", wildcards=[])
         self.node = ElementNode(
             position=0,
             meta=self.meta,
@@ -161,7 +165,9 @@ class ElementNodeTests(TestCase):
         mock_bind_content.return_value = False
         mock_bind_wild_content.side_effect = add_text
         mock_bind_objects.side_effect = add_child
-        find_wildcard.return_value = XmlVar(is_wildcard=True, qname="b", name="b")
+        find_wildcard.return_value = XmlVarFactory.create(
+            xml_type=XmlType.WILDCARD, qname="b", name="b"
+        )
 
         node = ElementNode(
             position=0,
@@ -255,7 +261,7 @@ class ElementNodeTests(TestCase):
         mock_bind_mixed_objects.assert_called_once_with(mock.ANY, wildcard, 0, objects)
 
     def test_child(self):
-        var = XmlVar(is_element=True, name="a", qname="a", types=(Foo,), dataclass=True)
+        var = XmlVarFactory.create(xml_type=XmlType.ELEMENT, qname="a", types=(Foo,))
         attrs = {"a": "b"}
         ns_map = {"ns0": "xsdata"}
         position = 1
@@ -268,10 +274,10 @@ class ElementNodeTests(TestCase):
         self.assertEqual(position, actual.position)
 
     def test_child_with_unique_element(self):
-        single = XmlVar(
-            is_element=True, name="a", qname="a", types=(Foo,), dataclass=True
+        single = XmlVarFactory.create(xml_type=XmlType.ELEMENT, qname="a", types=(Foo,))
+        wildcard = XmlVarFactory.create(
+            xml_type=XmlType.WILDCARD, qname="a", types=(object,)
         )
-        wildcard = XmlVar(is_wildcard=True, name="a", qname="a", types=(object,))
         self.meta.elements[single.qname] = [single]
         self.meta.wildcards.append(wildcard)
 
@@ -290,8 +296,8 @@ class ElementNodeTests(TestCase):
     @mock.patch.object(ElementNode, "build_node")
     def test_child_when_failed_to_build_next_node(self, mock_build_node):
         mock_build_node.return_value = None
-        element = XmlVar(is_element=True, name="a", qname="a")
-        wildcard = XmlVar(is_wildcard=True, name="a", qname="a")
+        element = XmlVarFactory.create(xml_type=XmlType.ELEMENT, qname="a")
+        wildcard = XmlVarFactory.create(xml_type=XmlType.WILDCARD, qname="a")
 
         self.meta.elements[element.qname] = [element]
         self.meta.wildcards.append(wildcard)
@@ -307,12 +313,11 @@ class ElementNodeTests(TestCase):
         self.assertIsInstance(actual, SkipNode)
 
     def test_build_node_with_dataclass_union_var(self):
-        var = XmlVar(
-            is_element=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENT,
             name="a",
             qname="a",
             types=(Foo, FooMixed),
-            dataclass=True,
         )
         attrs = {"a": "b"}
         ns_map = {"ns0": "xsdata"}
@@ -329,12 +334,11 @@ class ElementNodeTests(TestCase):
     @mock.patch.object(ParserUtils, "xsi_type", return_value="foo")
     @mock.patch.object(XmlContext, "fetch")
     def test_build_node_with_dataclass_var(self, mock_ctx_fetch, mock_xsi_type):
-        var = XmlVar(
-            is_element=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENT,
             name="a",
             qname="a",
             types=(Foo,),
-            dataclass=True,
             derived=True,
         )
         xsi_type = "foo"
@@ -356,9 +360,10 @@ class ElementNodeTests(TestCase):
 
     @mock.patch.object(XmlContext, "fetch")
     def test_build_node_with_dataclass_var_validates_nillable(self, mock_ctx_fetch):
-        var = XmlVar(is_element=True, name="a", qname="a", types=(Foo,), dataclass=True)
+        var = XmlVarFactory.create(xml_type=XmlType.ELEMENT, qname="a", types=(Foo,))
         ns_map = {}
-        nillable_meta = replace(self.meta, nillable=True)
+        nillable_meta = copy.deepcopy(self.meta)
+        nillable_meta.nillable = True
         mock_ctx_fetch.side_effect = [self.meta, self.meta, nillable_meta]
         attrs = {QNames.XSI_NIL: "false"}
 
@@ -371,8 +376,8 @@ class ElementNodeTests(TestCase):
         self.assertIsNone(self.node.build_node(var, attrs, ns_map, 10))
 
     def test_build_node_with_any_type_var_with_matching_xsi_type(self):
-        var = XmlVar(
-            is_element=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENT,
             name="a",
             qname="a",
             types=(object,),
@@ -390,8 +395,8 @@ class ElementNodeTests(TestCase):
         self.assertFalse(actual.mixed)
 
     def test_build_node_with_any_type_var_with_datatype(self):
-        var = XmlVar(
-            is_element=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENT,
             name="a",
             qname="a",
             types=(object,),
@@ -407,8 +412,8 @@ class ElementNodeTests(TestCase):
         self.assertEqual(var.derived, actual.derived)
 
     def test_build_node_with_any_type_var_with_no_matching_xsi_type(self):
-        var = XmlVar(
-            is_element=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENT,
             name="a",
             qname="a",
             types=(object,),
@@ -424,8 +429,8 @@ class ElementNodeTests(TestCase):
         self.assertEqual({}, actual.ns_map)
 
     def test_build_node_with_any_type_var_with_no_xsi_type(self):
-        var = XmlVar(
-            is_element=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENT,
             name="a",
             qname="a",
             types=(object,),
@@ -441,7 +446,7 @@ class ElementNodeTests(TestCase):
         self.assertEqual({}, actual.ns_map)
 
     def test_build_node_with_wildcard_var(self):
-        var = XmlVar(is_wildcard=True, name="a", qname="a", dataclass=False)
+        var = XmlVarFactory.create(xml_type=XmlType.WILDCARD, qname="a")
 
         actual = self.node.build_node(var, {}, {}, 10)
 
@@ -450,7 +455,9 @@ class ElementNodeTests(TestCase):
         self.assertEqual(var, actual.var)
 
     def test_build_node_with_primitive_var(self):
-        var = XmlVar(is_text=True, name="a", qname="a", types=(int,), default=100)
+        var = XmlVarFactory.create(
+            xml_type=XmlType.TEXT, qname="a", types=(int,), default=100
+        )
         attrs = {"a": "b"}
         ns_map = {"ns0": "xsdata"}
         actual = self.node.build_node(var, attrs, ns_map, 10)
@@ -475,7 +482,7 @@ class WildcardNodeTests(TestCase):
             children=[1, 2, 3],
         )
 
-        var = XmlVar(is_text=True, name="foo", qname="a")
+        var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo", qname="a")
         node = WildcardNode(position=0, var=var, attrs=attrs, ns_map=ns_map)
         objects = [("a", 1), ("b", 2), ("c", 3)]
 
@@ -501,7 +508,7 @@ class WildcardNodeTests(TestCase):
     def test_child(self):
         attrs = {"id": "1"}
         ns_map = {"ns0": "xsdata"}
-        var = XmlVar(is_text=True, name="foo", qname="foo")
+        var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo", qname="foo")
         node = WildcardNode(position=0, var=var, attrs={}, ns_map={})
         actual = node.child("foo", attrs, ns_map, 10)
 
@@ -517,7 +524,7 @@ class UnionNodeTests(TestCase):
         attrs = {"id": "1"}
         ns_map = {"ns0": "xsdata"}
         ctx = XmlContext()
-        var = XmlVar(is_text=True, name="foo", qname="foo")
+        var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo", qname="foo")
         node = UnionNode(position=0, var=var, context=ctx, attrs={}, ns_map={})
         self.assertEqual(node, node.child("foo", attrs, ns_map, 10))
 
@@ -527,7 +534,7 @@ class UnionNodeTests(TestCase):
 
     def test_bind_appends_end_event_when_level_not_zero(self):
         ctx = XmlContext()
-        var = XmlVar(is_text=True, name="foo", qname="foo")
+        var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo", qname="foo")
         node = UnionNode(position=0, var=var, context=ctx, attrs={}, ns_map={})
         node.level = 1
         objects = []
@@ -593,7 +600,9 @@ class PrimitiveNodeTests(TestCase):
     @mock.patch.object(ParserUtils, "parse_value")
     def test_bind(self, mock_parse_value):
         mock_parse_value.return_value = 13
-        var = XmlVar(is_text=True, name="foo", qname="foo", types=(int,), format="Nope")
+        var = XmlVarFactory.create(
+            xml_type=XmlType.TEXT, name="foo", qname="foo", types=(int,), format="Nope"
+        )
         ns_map = {"foo": "bar"}
         node = PrimitiveNode(var, ns_map)
         objects = []
@@ -606,7 +615,9 @@ class PrimitiveNodeTests(TestCase):
         )
 
     def test_bind_derived_mode(self):
-        var = XmlVar(is_text=True, name="foo", qname="foo", types=(int,), derived=True)
+        var = XmlVarFactory.create(
+            xml_type=XmlType.TEXT, name="foo", qname="foo", types=(int,), derived=True
+        )
         ns_map = {"foo": "bar"}
         node = PrimitiveNode(var, ns_map)
         objects = []
@@ -615,8 +626,8 @@ class PrimitiveNodeTests(TestCase):
         self.assertEqual(DerivedElement("foo", 13), objects[-1][1])
 
     def test_bind_nillable_content(self):
-        var = XmlVar(
-            is_text=True, name="foo", qname="foo", types=(str,), nillable=False
+        var = XmlVarFactory.create(
+            xml_type=XmlType.TEXT, name="foo", qname="foo", types=(str,), nillable=False
         )
         ns_map = {"foo": "bar"}
         node = PrimitiveNode(var, ns_map)
@@ -630,7 +641,7 @@ class PrimitiveNodeTests(TestCase):
         self.assertIsNone(objects[-1][1])
 
     def test_child(self):
-        var = XmlVar(is_text=True, name="foo", qname="foo")
+        var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo", qname="foo")
         node = PrimitiveNode(var, {})
 
         with self.assertRaises(XmlContextError):
@@ -815,7 +826,7 @@ class NodeParserTests(TestCase):
         parser = NodeParser()
         objects = [("q", "result")]
         queue = []
-        var = XmlVar(is_text=True, name="foo", qname="foo")
+        var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo", qname="foo")
         queue.append(PrimitiveNode(var, ns_map={}))
 
         result = parser.end(queue, objects, "author", "foobar", None)
diff --git a/tests/formats/dataclass/parsers/test_utils.py b/tests/formats/dataclass/parsers/test_utils.py
index 670460ee..cecd1ed9 100644
--- a/tests/formats/dataclass/parsers/test_utils.py
+++ b/tests/formats/dataclass/parsers/test_utils.py
@@ -1,9 +1,7 @@
 from dataclasses import field
 from dataclasses import make_dataclass
-from dataclasses import replace
 from typing import Any
 from unittest import mock
-from unittest.case import TestCase
 
 from tests.fixtures.books import Books
 from tests.fixtures.defxmlschema.chapter12 import ProductType
@@ -12,16 +10,18 @@ from xsdata.formats.converter import ConverterFactory
 from xsdata.formats.dataclass.context import XmlContext
 from xsdata.formats.dataclass.models.elements import XmlMeta
 from xsdata.formats.dataclass.models.elements import XmlType
-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.utils import ParserUtils
 from xsdata.models.enums import Namespace
 from xsdata.models.enums import QNames
+from xsdata.utils.testing import FactoryTestCase
+from xsdata.utils.testing import XmlVarFactory
 
 
-class ParserUtilsTests(TestCase):
+class ParserUtilsTests(FactoryTestCase):
     def setUp(self) -> None:
+        super().setUp()
         self.ctx = XmlContext()
 
     def test_xsi_type(self):
@@ -146,7 +146,9 @@ class ParserUtilsTests(TestCase):
             (None, "foo"),
         ]
 
-        var = XmlVar(is_wildcard=True, name="foo", qname="{any}foo")
+        var = XmlVarFactory.create(
+            xml_type=XmlType.WILDCARD, name="foo", qname="{any}foo"
+        )
         params = {}
         ParserUtils.bind_mixed_objects(params, var, 1, objects)
 
@@ -224,7 +226,7 @@ class ParserUtilsTests(TestCase):
     def test_bind_attrs_ignore_init_false_vars(self):
         metadata = self.ctx.build(ProductType)
         eff_date = metadata.find_attribute("effDate")
-        eff_date = replace(eff_date, init=False)
+        eff_date.init = False
 
         metadata.attributes[eff_date.qname] = eff_date
         params = {}
@@ -272,7 +274,7 @@ class ParserUtilsTests(TestCase):
         self.assertEqual({}, params)
 
     def test_bind_var(self):
-        var = XmlVar(name="a", qname="a", is_element=True)
+        var = XmlVarFactory.create(name="a", qname="a", xml_type=XmlType.ELEMENT)
         params = {}
 
         status = ParserUtils.bind_var(params, var, 1)
@@ -290,7 +292,9 @@ class ParserUtilsTests(TestCase):
         self.assertEqual({}, params)
 
     def test_bind_var_with_list_var(self):
-        var = XmlVar(name="a", qname="a", list_element=True, is_element=True)
+        var = XmlVarFactory.create(
+            name="a", qname="a", list_element=True, xml_type=XmlType.ELEMENT
+        )
         params = {}
 
         status = ParserUtils.bind_var(params, var, 1)
@@ -303,7 +307,7 @@ class ParserUtilsTests(TestCase):
 
     def test_bind_wild_var(self):
         params = {}
-        var = XmlVar(name="a", qname="a", is_wildcard=True)
+        var = XmlVarFactory.create(name="a", qname="a", xml_type=XmlType.WILDCARD)
         qname = "b"
         one = AnyElement(qname=qname, text="one")
         two = AnyElement(qname=qname, text="two")
@@ -321,7 +325,9 @@ class ParserUtilsTests(TestCase):
 
     def test_bind_wild_var_with_list_var(self):
         params = {}
-        var = XmlVar(name="a", qname="a", list_element=True, is_element=True)
+        var = XmlVarFactory.create(
+            name="a", qname="a", list_element=True, xml_type=XmlType.ELEMENT
+        )
         qname = "b"
         one = AnyElement(qname=qname, text="one")
         two = AnyElement(qname=qname, text="two")
@@ -334,7 +340,7 @@ class ParserUtilsTests(TestCase):
         self.assertEqual(dict(a=[one, two, three]), params)
 
     def test_bind_wild_content(self):
-        var = XmlVar(name="a", qname="a", is_wildcard=True)
+        var = XmlVarFactory.create(name="a", qname="a", xml_type=XmlType.WILDCARD)
         params = {}
         attrs = {}
         ns_map = {}
@@ -356,12 +362,12 @@ class ParserUtilsTests(TestCase):
         self.assertEqual(dict(a=expected), params)
 
     def test_bind_wildcard_when_var_is_list(self):
-        var = XmlVar(
+        var = XmlVarFactory.create(
             name="a",
             qname="a",
             default=list,
             list_element=True,
-            is_wildcard=True,
+            xml_type=XmlType.WILDCARD,
         )
         params = {}
         attrs = {"a", "b"}
diff --git a/tests/formats/dataclass/parsers/test_xml.py b/tests/formats/dataclass/parsers/test_xml.py
index 3a5553c1..1cd16a4c 100644
--- a/tests/formats/dataclass/parsers/test_xml.py
+++ b/tests/formats/dataclass/parsers/test_xml.py
@@ -2,14 +2,16 @@ from unittest import mock
 from unittest.case import TestCase
 
 from tests.fixtures.books import Books
-from xsdata.formats.dataclass.models.elements import XmlVar
+from xsdata.formats.dataclass.models.elements import XmlType
 from xsdata.formats.dataclass.parsers.nodes import PrimitiveNode
 from xsdata.formats.dataclass.parsers.nodes import SkipNode
 from xsdata.formats.dataclass.parsers.xml import UserXmlParser
 from xsdata.models.enums import EventType
+from xsdata.utils.testing import FactoryTestCase
+from xsdata.utils.testing import XmlVarFactory
 
 
-class UserXmlParserTests(TestCase):
+class UserXmlParserTests(FactoryTestCase):
     def setUp(self):
         super().setUp()
         self.parser = UserXmlParser()
@@ -31,7 +33,7 @@ class UserXmlParserTests(TestCase):
     def test_end(self, mock_emit_event):
         objects = []
         queue = []
-        var = XmlVar(is_text=True, name="foo", qname="foo", types=(bool,))
+        var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo", types=(bool,))
         queue.append(PrimitiveNode(var, {}))
 
         result = self.parser.end(queue, objects, "enabled", "true", None)
diff --git a/tests/formats/dataclass/serializers/test_xml.py b/tests/formats/dataclass/serializers/test_xml.py
index 33c6eaa7..e7abb77a 100644
--- a/tests/formats/dataclass/serializers/test_xml.py
+++ b/tests/formats/dataclass/serializers/test_xml.py
@@ -12,13 +12,13 @@ from tests.fixtures.books import BookForm
 from xsdata.exceptions import SerializerError
 from xsdata.exceptions import XmlContextError
 from xsdata.formats.dataclass.models.elements import XmlType
-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.serializers import XmlSerializer
 from xsdata.formats.dataclass.serializers.mixins import XmlWriterEvent
 from xsdata.models.enums import DataType
 from xsdata.models.enums import QNames
+from xsdata.utils.testing import XmlVarFactory
 
 
 @dataclass
@@ -105,7 +105,7 @@ class XmlSerializerTests(TestCase):
         self.assertEqual("Object <class 'int'> is not a dataclass.", str(cm.exception))
 
     def test_write_mixed_content(self):
-        var = XmlVar(is_wildcard=True, qname="a", name="a", mixed=True)
+        var = XmlVarFactory.create(xml_type=XmlType.WILDCARD, qname="a", mixed=True)
         book = BookForm(id="123")
         ebook = DerivedElement("ebook", BookForm(id="123"))
         value = ["text", AnyElement(qname="br"), book, ebook, "tail"]
@@ -130,7 +130,7 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_data(self):
-        var = XmlVar(is_text=True, qname="a", name="a")
+        var = XmlVarFactory.create(xml_type=XmlType.TEXT, qname="a")
         expected = [(XmlWriterEvent.DATA, "123")]
 
         result = self.serializer.write_value("123", var, "xsdata")
@@ -138,7 +138,7 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_tokens(self):
-        var = XmlVar(is_element=True, qname="a", name="a", tokens=True)
+        var = XmlVarFactory.create(xml_type=XmlType.ELEMENT, qname="a", tokens=True)
 
         result = self.serializer.write_value([], var, "xsdata")
         self.assertIsInstance(result, Generator)
@@ -164,7 +164,9 @@ class XmlSerializerTests(TestCase):
         result = self.serializer.write_value([[1, 2, 3], [4, 5, 6]], var, "xsdata")
         self.assertEqual(expected, list(result))
 
-        var = XmlVar(is_element=True, qname="a", name="a", tokens=True, nillable=True)
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENT, qname="a", tokens=True, nillable=True
+        )
         expected = [
             (XmlWriterEvent.START, "a"),
             (XmlWriterEvent.ATTR, QNames.XSI_NIL, "true"),
@@ -176,7 +178,7 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_any_type_with_primitive(self):
-        var = XmlVar(is_wildcard=True, qname="a", name="a")
+        var = XmlVarFactory.create(xml_type=XmlType.WILDCARD, qname="a")
         expected = [(XmlWriterEvent.DATA, "str")]
 
         result = self.serializer.write_value("str", var, "xsdata")
@@ -184,7 +186,7 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_any_type_with_primitive_element(self):
-        var = XmlVar(is_element=True, qname="a", name="a", types=(object,))
+        var = XmlVarFactory.create(xml_type=XmlType.ELEMENT, qname="a", types=(object,))
         expected = [
             (XmlWriterEvent.START, "a"),
             (XmlWriterEvent.DATA, "str"),
@@ -196,7 +198,7 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_any_type_with_any_element(self):
-        var = XmlVar(is_wildcard=True, qname="a", name="a")
+        var = XmlVarFactory.create(xml_type=XmlType.WILDCARD, qname="a")
         value = AnyElement(
             qname="a",
             text="b",
@@ -220,7 +222,7 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_any_type_with_derived_element_primitive(self):
-        var = XmlVar(is_wildcard=True, qname="a", name="a")
+        var = XmlVarFactory.create(xml_type=XmlType.WILDCARD, qname="a")
         value = DerivedElement(qname="a", value=1)
         expected = [
             (XmlWriterEvent.START, "a"),
@@ -234,7 +236,7 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_any_type_with_derived_element_dataclass(self):
-        var = XmlVar(is_wildcard=True, qname="a", name="a")
+        var = XmlVarFactory.create(xml_type=XmlType.WILDCARD, qname="a")
         value = DerivedElement(qname="a", value=BookForm(title="def"), substituted=True)
         expected = [
             (XmlWriterEvent.START, "a"),
@@ -251,11 +253,10 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_xsi_type(self):
-        var = XmlVar(
-            is_element=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENT,
             qname="a",
             name="a",
-            dataclass=True,
             types=(BookForm,),
         )
         value = BookForm(id="123")
@@ -271,11 +272,10 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_xsi_type_with_derived_class(self):
-        var = XmlVar(
-            is_element=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENT,
             qname="a",
             name="a",
-            dataclass=True,
             types=(BookForm,),
         )
         ebook = make_dataclass("eBook", [], bases=(BookForm,))
@@ -294,11 +294,10 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_xsi_type_with_illegal_derived_class(self):
-        var = XmlVar(
-            is_element=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENT,
             qname="a",
             name="a",
-            dataclass=True,
             types=(BookForm,),
         )
         ebook = make_dataclass("eBook", [])
@@ -311,7 +310,7 @@ class XmlSerializerTests(TestCase):
         self.assertEqual("eBook is not derived from BookForm", str(cm.exception))
 
     def test_write_element(self):
-        var = XmlVar(is_element=True, qname="a", name="a")
+        var = XmlVarFactory.create(xml_type=XmlType.ELEMENT, qname="a")
         expected = [
             (XmlWriterEvent.START, "a"),
             (XmlWriterEvent.DATA, "123"),
@@ -323,7 +322,7 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_element_with_nillable_true(self):
-        var = XmlVar(is_element=True, qname="a", name="a", nillable=True)
+        var = XmlVarFactory.create(xml_type=XmlType.ELEMENT, qname="a", nillable=True)
         expected = [
             (XmlWriterEvent.START, "a"),
             (XmlWriterEvent.ATTR, QNames.XSI_NIL, "true"),
@@ -336,8 +335,8 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_element_with_any_type_var(self):
-        var = XmlVar(
-            is_element=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENT,
             qname="a",
             name="a",
             types=(object,),
@@ -355,8 +354,8 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_element_with_any_type_var_ignore_xs_string(self):
-        var = XmlVar(
-            is_element=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENT,
             qname="a",
             name="a",
             types=(object,),
@@ -381,11 +380,15 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_choice_with_derived_primitive_value(self):
-        var = XmlVar(
-            is_elements=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENTS,
             name="compound",
             qname="compound",
-            elements={"a": XmlVar(is_element=True, qname="a", name="a", types=(int,))},
+            elements={
+                "a": XmlVarFactory.create(
+                    xml_type=XmlType.ELEMENT, qname="a", types=(int,)
+                )
+            },
         )
         value = DerivedElement(qname="a", value=1)
         expected = [
@@ -399,11 +402,14 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_choice_with_derived_dataclass(self):
-        var = XmlVar(
-            is_elements=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENTS,
             name="compound",
-            qname="compound",
-            elements={"a": XmlVar(is_element=True, qname="a", name="a", types=(A,))},
+            elements={
+                "a": XmlVarFactory.create(
+                    xml_type=XmlType.ELEMENT, qname="a", types=(A,)
+                )
+            },
         )
         value = DerivedElement(qname="a", value=A("foo"))
         expected = [
@@ -417,11 +423,15 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_choice_with_generic_object(self):
-        var = XmlVar(
-            is_elements=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENTS,
             name="compound",
             qname="compound",
-            elements={"a": XmlVar(is_element=True, qname="a", name="a", types=(int,))},
+            elements={
+                "a": XmlVarFactory.create(
+                    xml_type=XmlType.ELEMENT, qname="a", types=(int,)
+                )
+            },
         )
         value = AnyElement(qname="a", text="1")
         expected = [
@@ -435,14 +445,16 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_choice_with_raw_value(self):
-        var = XmlVar(
-            is_elements=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENTS,
             name="compound",
             qname="compound",
             elements={
-                "a": XmlVar(is_element=True, qname="a", name="a", types=(int,)),
-                "b": XmlVar(
-                    is_element=True,
+                "a": XmlVarFactory.create(
+                    xml_type=XmlType.ELEMENT, qname="a", types=(int,)
+                ),
+                "b": XmlVarFactory.create(
+                    xml_type=XmlType.ELEMENT,
                     qname="b",
                     name="b",
                     types=(int,),
@@ -464,12 +476,14 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(expected, list(result))
 
     def test_write_choice_when_no_matching_choice_exists(self):
-        var = XmlVar(
-            is_elements=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENTS,
             name="compound",
             qname="compound",
             elements={
-                "a": XmlVar(is_element=True, qname="a", name="a", types=(float,))
+                "a": XmlVarFactory.create(
+                    xml_type=XmlType.ELEMENT, qname="a", types=(float,)
+                )
             },
         )
 
@@ -481,7 +495,9 @@ class XmlSerializerTests(TestCase):
         self.assertEqual(msg, str(cm.exception))
 
     def test_write_value_with_list_value(self):
-        var = XmlVar(is_element=True, qname="a", name="a", list_element=True)
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENT, qname="a", list_element=True
+        )
         value = [True, False]
         expected = [
             (XmlWriterEvent.START, "a"),
diff --git a/tests/formats/dataclass/test_context.py b/tests/formats/dataclass/test_context.py
index 4ba5233c..75552abf 100644
--- a/tests/formats/dataclass/test_context.py
+++ b/tests/formats/dataclass/test_context.py
@@ -1,18 +1,19 @@
+import copy
 from dataclasses import fields
 from dataclasses import make_dataclass
 from dataclasses import replace
 from unittest import mock
-from unittest import TestCase
 
 from tests.fixtures.books import BookForm
 from tests.fixtures.books import BooksForm
 from tests.fixtures.defxmlschema.chapter13 import ItemsType
 from xsdata.formats.dataclass.context import XmlContext
-from xsdata.formats.dataclass.models.elements import XmlMeta
 from xsdata.models.enums import DataType
+from xsdata.utils.testing import FactoryTestCase
+from xsdata.utils.testing import XmlMetaFactory
 
 
-class XmlContextTests(TestCase):
+class XmlContextTests(FactoryTestCase):
     def setUp(self):
         self.ctx = XmlContext()
         super().setUp()
@@ -20,12 +21,7 @@ class XmlContextTests(TestCase):
     @mock.patch.object(XmlContext, "find_subclass")
     @mock.patch.object(XmlContext, "build")
     def test_fetch(self, mock_build, mock_find_subclass):
-        meta = XmlMeta(
-            clazz=ItemsType,
-            qname="ItemsType",
-            source_qname="ItemsType",
-            nillable=False,
-        )
+        meta = XmlMetaFactory.create(clazz=ItemsType, qname="ItemsType")
         mock_build.return_value = meta
         actual = self.ctx.fetch(ItemsType, "foo")
         self.assertEqual(meta, actual)
@@ -37,7 +33,7 @@ class XmlContextTests(TestCase):
     def test_fetch_with_xsi_type_and_subclass_not_found(
         self, mock_build, mock_find_subclass
     ):
-        meta = XmlMeta(
+        meta = XmlMetaFactory.create(
             clazz=ItemsType,
             qname="ItemsType",
             source_qname="ItemsType",
@@ -55,13 +51,9 @@ class XmlContextTests(TestCase):
     def test_fetch_with_xsi_type_and_subclass_found(
         self, mock_build, mock_find_subclass
     ):
-        meta = XmlMeta(
-            clazz=ItemsType,
-            qname="ItemsType",
-            source_qname="ItemsType",
-            nillable=False,
-        )
-        xsi_meta = replace(meta, qname="XsiType")
+        meta = XmlMetaFactory.create(clazz=ItemsType)
+        xsi_meta = copy.deepcopy(meta)
+        xsi_meta.qname = "XsiType"
 
         mock_build.side_effect = [meta, xsi_meta]
         mock_find_subclass.return_value = xsi_meta
diff --git a/tests/formats/dataclass/test_elements.py b/tests/formats/dataclass/test_elements.py
index cfcdc741..8c4005d7 100644
--- a/tests/formats/dataclass/test_elements.py
+++ b/tests/formats/dataclass/test_elements.py
@@ -1,11 +1,14 @@
+import copy
 from dataclasses import dataclass
 from dataclasses import make_dataclass
 from typing import Iterator
 from unittest import mock
 from unittest.case import TestCase
 
-from xsdata.formats.dataclass.models.elements import XmlMeta
+from xsdata.formats.dataclass.models.elements import XmlType
 from xsdata.formats.dataclass.models.elements import XmlVar
+from xsdata.utils.testing import XmlMetaFactory
+from xsdata.utils.testing import XmlVarFactory
 
 
 @dataclass
@@ -14,39 +17,41 @@ class Fixture:
 
 
 class XmlValTests(TestCase):
-    def test_property_lname(self):
-        var = XmlVar(name="a", qname="{B}A")
-        self.assertEqual("A", var.lname)
+    def test_property_local_name(self):
+        var = XmlVarFactory.create(name="a", qname="{B}A")
+        self.assertEqual("A", var.local_name)
 
     def test_property_clazz(self):
-        var = XmlVar(name="foo", qname="foo")
+        var = XmlVarFactory.create(name="foo")
         self.assertIsNone(var.clazz)
 
-        var = XmlVar(name="foo", qname="foo", dataclass=True, types=(Fixture,))
+        var = XmlVarFactory.create(name="foo", types=(Fixture,))
         self.assertEqual(Fixture, var.clazz)
 
+    def test_get_xml_type(self):
+        for xml_type in XmlType.all():
+            var = XmlVarFactory.create(name="foo", xml_type=xml_type)
+            self.assertEqual(xml_type, var.get_xml_type())
+
     def test_property_is_clazz_union(self):
-        var = XmlVar(name="foo", qname="foo", dataclass=True, types=(Fixture,))
+        var = XmlVarFactory.create(name="foo", types=(Fixture,))
         self.assertFalse(var.is_clazz_union)
 
-        var.types = var.types + (int,)
+        var = XmlVarFactory.create(name="foo", types=(Fixture, int))
         self.assertTrue(var.is_clazz_union)
 
-    def test_property_is_list(self):
-        var = XmlVar(name="foo", qname="foo")
-        self.assertFalse(var.list_element)
-
-        var = XmlVar(name="foo", qname="foo", types=(int,), list_element=True)
-        self.assertTrue(var.list_element)
-
     def test_find_choice(self):
-        var = XmlVar(
-            is_elements=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENTS,
             name="foo",
             qname="foo",
             elements={
-                "{a}a": XmlVar(is_element=True, name="a", qname="{a}a"),
-                "b": XmlVar(is_element=True, name="b", qname="b"),
+                "{a}a": XmlVarFactory.create(
+                    xml_type=XmlType.ELEMENT, name="a", qname="{a}a"
+                ),
+                "b": XmlVarFactory.create(
+                    xml_type=XmlType.ELEMENT, name="b", qname="b"
+                ),
             },
         )
 
@@ -56,10 +61,16 @@ class XmlValTests(TestCase):
 
         var.elements.clear()
         var.wildcards = [
-            XmlVar(
-                is_wildcard=True, name="target", qname="target", namespaces=("foo",)
+            XmlVarFactory.create(
+                xml_type=XmlType.WILDCARD,
+                name="target",
+                namespaces=("foo",),
+            ),
+            XmlVarFactory.create(
+                xml_type=XmlType.WILDCARD,
+                name="other",
+                namespaces=("!foo",),
             ),
-            XmlVar(is_wildcard=True, name="other", qname="other", namespaces=("!foo",)),
         ]
 
         self.assertEqual(var.wildcards[1], var.find_choice("{a}a"))
@@ -70,16 +81,26 @@ class XmlValTests(TestCase):
         d = make_dataclass("D", fields=[], bases=(c,))
 
         elements = [
-            XmlVar(is_element=True, qname="a", name="a", types=(int,)),
-            XmlVar(is_element=True, qname="b", name="b", types=(int,), tokens=True),
-            XmlVar(is_element=True, qname="c", name="c", types=(c,), dataclass=True),
-            XmlVar(is_element=True, qname="d", name="d", types=(float,), nillable=True),
+            XmlVarFactory.create(xml_type=XmlType.ELEMENT, name="a", types=(int,)),
+            XmlVarFactory.create(
+                xml_type=XmlType.ELEMENT, name="b", types=(int,), tokens=True
+            ),
+            XmlVarFactory.create(
+                xml_type=XmlType.ELEMENT,
+                name="c",
+                types=(c,),
+            ),
+            XmlVarFactory.create(
+                xml_type=XmlType.ELEMENT,
+                name="d",
+                types=(float,),
+                nillable=True,
+            ),
         ]
 
-        var = XmlVar(
-            is_elements=True,
+        var = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENTS,
             name="compound",
-            qname="compound",
             elements={x.qname: x for x in elements},
         )
 
@@ -91,38 +112,89 @@ class XmlValTests(TestCase):
         self.assertEqual(elements[3], var.find_value_choice(None))
 
     def test_match_namespace(self):
-        var = XmlVar(is_wildcard=True, name="foo", qname="foo")
+        var = XmlVarFactory.create(xml_type=XmlType.WILDCARD, name="foo")
         self.assertTrue(var.match_namespace("*"))
         self.assertTrue(var.match_namespace("a"))
 
-        var = XmlVar(is_wildcard=True, name="foo", qname="foo", namespaces=("tns",))
+        var = XmlVarFactory.create(
+            xml_type=XmlType.WILDCARD, name="foo", namespaces=("tns",)
+        )
         self.assertFalse(var.match_namespace("a"))
         self.assertTrue(var.match_namespace("{tns}a"))
 
-        var = XmlVar(is_wildcard=True, name="foo", qname="foo", namespaces=("##any",))
+        var = XmlVarFactory.create(
+            xml_type=XmlType.WILDCARD, name="foo", namespaces=("##any",)
+        )
         self.assertTrue(var.match_namespace("a"))
         self.assertTrue(var.match_namespace("{tns}a"))
 
-        var = XmlVar(is_wildcard=True, name="foo", qname="foo", namespaces=("",))
+        var = XmlVarFactory.create(
+            xml_type=XmlType.WILDCARD, name="foo", namespaces=("",)
+        )
         self.assertTrue(var.match_namespace("a"))
         self.assertFalse(var.match_namespace("{tns}a"))
 
-        var = XmlVar(is_wildcard=True, name="foo", qname="foo", namespaces=("!tns",))
+        var = XmlVarFactory.create(
+            xml_type=XmlType.WILDCARD, name="foo", namespaces=("!tns",)
+        )
         self.assertTrue(var.match_namespace("{foo}a"))
         self.assertFalse(var.match_namespace("{tns}a"))
 
         var.namespace_matches["{tns}cached"] = True
         self.assertTrue(var.match_namespace("{tns}cached"))
 
+    def test_eq(self):
+        var = XmlVarFactory.create(name="foo", types=(int, float))
+        clone = copy.deepcopy(var)
+        self.assertEqual(var, clone)
+
+        clone.default = 1
+        self.assertNotEqual(var, clone)
+        self.assertNotEqual(var, 1)
+        self.assertNotEqual(1, var)
+
+    def test_repr(self):
+        var = XmlVarFactory.create(name="foo", types=(int, float))
+        expected = (
+            "XmlVar("
+            "index=0, "
+            "name=foo, "
+            "qname=foo, "
+            "types=(<class 'int'>, <class 'float'>), "
+            "init=True, "
+            "mixed=False, "
+            "tokens=False, "
+            "format=None, "
+            "derived=False, "
+            "any_type=False, "
+            "nillable=False, "
+            "sequential=False, "
+            "list_element=False, "
+            "default=None, "
+            "xml_type=Element, "
+            "namespaces=(), "
+            "elements={}, "
+            "wildcards=[])"
+        )
+        self.assertEqual(expected, repr(var))
+
 
 class XmlMetaTests(TestCase):
     def setUp(self) -> None:
         a = make_dataclass("a", [])
-        self.meta = XmlMeta(clazz=a, qname="a", source_qname="a", nillable=False)
+        self.meta = XmlMetaFactory.create(
+            clazz=a,
+            qname="a",
+            choices=[],
+            wildcards=[],
+            attributes={},
+            elements={},
+            any_attributes=[],
+        )
 
     def test_find_attribute(self):
-        a = XmlVar(is_attribute=True, qname="a", name="a")
-        b = XmlVar(is_attribute=True, qname="b", name="b")
+        a = XmlVarFactory.create(xml_type=XmlType.ATTRIBUTE, name="a")
+        b = XmlVarFactory.create(xml_type=XmlType.ATTRIBUTE, name="b")
 
         self.meta.attributes[a.qname] = a
         self.meta.attributes[b.qname] = b
@@ -131,8 +203,8 @@ class XmlMetaTests(TestCase):
         self.assertEqual(b, self.meta.find_attribute("b"))
 
     def test_find_elements(self):
-        a_1 = XmlVar(is_attribute=True, qname="a", name="a_1")
-        a_2 = XmlVar(is_attribute=True, qname="a", name="a_2")
+        a_1 = XmlVarFactory.create(xml_type=XmlType.ATTRIBUTE, qname="a", name="a_1")
+        a_2 = XmlVarFactory.create(xml_type=XmlType.ATTRIBUTE, qname="a", name="a_2")
 
         self.meta.elements[a_1.qname] = [a_1, a_2]
 
@@ -141,12 +213,16 @@ class XmlMetaTests(TestCase):
 
     @mock.patch.object(XmlVar, "find_choice")
     def test_find_choice(self, mock_find_choice):
-        a_1 = XmlVar(is_attribute=True, qname="a", name="a_1")
-        a_2 = XmlVar(is_attribute=True, qname="a", name="a_2")
+        a_1 = XmlVarFactory.create(xml_type=XmlType.ATTRIBUTE, qname="a", name="a_1")
+        a_2 = XmlVarFactory.create(xml_type=XmlType.ATTRIBUTE, qname="a", name="a_2")
 
         mock_find_choice.side_effect = [None, None, None, a_1, a_2]
-        choice_1 = XmlVar(is_attribute=True, qname="compound_1", name="compound_1")
-        choice_2 = XmlVar(is_attribute=True, qname="compound_2", name="compound_2")
+        choice_1 = XmlVarFactory.create(
+            xml_type=XmlType.ATTRIBUTE, qname="compound_1", name="compound_1"
+        )
+        choice_2 = XmlVarFactory.create(
+            xml_type=XmlType.ATTRIBUTE, qname="compound_2", name="compound_2"
+        )
         self.meta.choices = [choice_1, choice_2]
 
         self.assertIsNone(self.meta.find_choice("a"))
@@ -160,8 +236,10 @@ class XmlMetaTests(TestCase):
         mock_match_namespace.side_effect = [False, False, False, True]
 
         attributes = [
-            XmlVar(is_attributes=True, qname="any", name="any"),
-            XmlVar(is_attributes=True, qname="other", name="any"),
+            XmlVarFactory.create(xml_type=XmlType.ATTRIBUTES, qname="any", name="any"),
+            XmlVarFactory.create(
+                xml_type=XmlType.ATTRIBUTES, qname="other", name="any"
+            ),
         ]
         self.meta.any_attributes = attributes
 
@@ -175,8 +253,8 @@ class XmlMetaTests(TestCase):
         mock_match_namespace.side_effect = [False, False, False, True]
 
         wildcards = [
-            XmlVar(is_wildcard=True, qname="any", name="any"),
-            XmlVar(is_wildcard=True, qname="other", name="any"),
+            XmlVarFactory.create(xml_type=XmlType.WILDCARD, qname="any", name="any"),
+            XmlVarFactory.create(xml_type=XmlType.WILDCARD, qname="other", name="any"),
         ]
         self.meta.wildcards = wildcards
 
@@ -186,14 +264,14 @@ class XmlMetaTests(TestCase):
         mock_match_namespace.assert_has_calls([mock.call("a") for _ in range(4)])
 
     def test_find_children(self):
-        element1 = XmlVar(is_element=True, qname="a", name="a")
-        element2 = XmlVar(is_element=True, qname="a", name="a1")
+        element1 = XmlVarFactory.create(xml_type=XmlType.ELEMENT, name="a")
+        element2 = XmlVarFactory.create(xml_type=XmlType.ELEMENT, qname="a", name="a1")
 
-        option1 = XmlVar(is_element=True, qname="a", name="a3")
-        option2 = XmlVar(is_element=True, qname="b", name="b")
+        option1 = XmlVarFactory.create(xml_type=XmlType.ELEMENT, qname="a", name="a3")
+        option2 = XmlVarFactory.create(xml_type=XmlType.ELEMENT, name="b")
 
-        choice = XmlVar(
-            is_elements=True,
+        choice = XmlVarFactory.create(
+            xml_type=XmlType.ELEMENTS,
             qname="c1",
             name="c1",
             elements={
@@ -201,7 +279,9 @@ class XmlMetaTests(TestCase):
                 "b": option2,
             },
         )
-        wildcard = XmlVar(is_wildcard=True, qname="any", name="any")
+        wildcard = XmlVarFactory.create(
+            xml_type=XmlType.WILDCARD, qname="any", name="any"
+        )
 
         self.meta.elements["a"] = [element1, element2]
         self.meta.choices.append(choice)
@@ -210,3 +290,19 @@ class XmlMetaTests(TestCase):
         result = self.meta.find_children("a")
         self.assertIsInstance(result, Iterator)
         self.assertEqual([element1, element2, option1, wildcard], list(result))
+
+    def test_repr(self):
+        expected = (
+            "XmlMeta("
+            "clazz=<class 'types.a'>, "
+            "qname=a, "
+            "source_qname=a, "
+            "nillable=False, "
+            "text=None, "
+            "choices=[], "
+            "elements={}, "
+            "wildcards=[], "
+            "attributes={}, "
+            "any_attributes=[])"
+        )
+        self.assertEqual(expected, repr(self.meta))
diff --git a/xsdata/utils/testing.py b/xsdata/utils/testing.py
index a695c0d7..2d1148b0 100644
--- a/xsdata/utils/testing.py
+++ b/xsdata/utils/testing.py
@@ -1,16 +1,15 @@
+import abc
 import copy
 import importlib
 import random
 import unittest
-from abc import ABC
-from abc import abstractmethod
 from typing import Any
 from typing import Dict
 from typing import List
 from typing import Optional
+from typing import Sequence
 from typing import Type
 from typing import TypeVar
-from typing import Union
 
 from xsdata.codegen.models import Attr
 from xsdata.codegen.models import AttrType
@@ -19,6 +18,9 @@ from xsdata.codegen.models import Extension
 from xsdata.codegen.models import Import
 from xsdata.codegen.models import Restrictions
 from xsdata.codegen.models import Status
+from xsdata.formats.dataclass.models.elements import XmlMeta
+from xsdata.formats.dataclass.models.elements import XmlType
+from xsdata.formats.dataclass.models.elements import XmlVar
 from xsdata.models.enums import DataType
 from xsdata.models.enums import Namespace
 from xsdata.models.enums import Tag
@@ -26,7 +28,6 @@ from xsdata.utils.namespaces import build_qname
 
 T = TypeVar("T")
 
-
 DEFAULT_NS_MAP = {
     Namespace.XS.prefix: Namespace.XS.uri,
     Namespace.XSI.prefix: Namespace.XSI.uri,
@@ -58,15 +59,17 @@ class FactoryTestCase(unittest.TestCase):
         AttrTypeFactory.reset()
         ExtensionFactory.reset()
         PackageFactory.reset()
+        XmlVarFactory.reset()
+        XmlMetaFactory.reset()
 
 
-class Factory(ABC):
+class Factory(abc.ABC):
     counter = 0
     model: Type
 
     @classmethod
-    @abstractmethod
-    def create(cls, **kwargs: Any) -> Union[Attr, AttrType, Class, Extension, Import]:
+    @abc.abstractmethod
+    def create(cls, **kwargs: Any) -> Any:
         """Abstract method create."""
 
     @classmethod
@@ -317,5 +320,121 @@ class PackageFactory(Factory):
         alias: Optional[str] = None,
         **kwargs: Any,
     ) -> Import:
-
         return Import(name=name, source=source, alias=alias)
+
+
+class XmlVarFactory(Factory):
+    counter = 65
+
+    @classmethod
+    def create(
+        cls,
+        name: Optional[str] = None,
+        qname: Optional[str] = None,
+        index: int = 0,
+        types: Optional[Sequence[Type]] = None,
+        init: bool = True,
+        mixed: bool = False,
+        tokens: bool = False,
+        format: Optional[str] = None,
+        derived: bool = False,
+        any_type: bool = False,
+        nillable: bool = False,
+        sequential: bool = False,
+        list_element: bool = False,
+        default: Any = None,
+        xml_type: str = XmlType.ELEMENT,
+        namespaces: Optional[Sequence[str]] = None,
+        elements: Optional[Dict[str, XmlVar]] = None,
+        wildcards: Optional[Sequence[XmlVar]] = None,
+        **kwargs: Any,
+    ) -> XmlVar:
+
+        name = name or f"field_{cls.next_letter()}"
+
+        if qname is None:
+            qname = name
+
+        if types is None:
+            types = ()
+        if namespaces is None:
+            namespaces = ()
+        if elements is None:
+            elements = {}
+        if wildcards is None:
+            wildcards = []
+
+        return XmlVar(
+            index=index,
+            name=name,
+            qname=qname,
+            types=types,
+            init=init,
+            mixed=mixed,
+            tokens=tokens,
+            format=format,
+            derived=derived,
+            any_type=any_type,
+            nillable=nillable,
+            sequential=sequential,
+            list_element=list_element,
+            default=default,
+            xml_type=xml_type,
+            namespaces=namespaces,
+            elements=elements,
+            wildcards=wildcards,
+        )
+
+
+class XmlMetaFactory(Factory):
+    counter = 65
+
+    @classmethod
+    def create(  # type: ignore
+        cls,
+        clazz: Type,
+        qname: Optional[str] = None,
+        source_qname: Optional[str] = None,
+        nillable: bool = False,
+        text: Optional[XmlVar] = None,
+        choices: Optional[Sequence[XmlVar]] = None,
+        elements: Optional[Dict[str, Sequence[XmlVar]]] = None,
+        wildcards: Optional[Sequence[XmlVar]] = None,
+        attributes: Optional[Dict[str, XmlVar]] = None,
+        any_attributes: Optional[Sequence[XmlVar]] = None,
+        **kwargs: Any,
+    ) -> XmlMeta:
+
+        if qname is None:
+            qname = clazz.__name__
+
+        if source_qname is None:
+            source_qname = qname
+
+        if choices is None:
+            choices = []
+
+        if elements is None:
+            elements = {}
+
+        if wildcards is None:
+            wildcards = []
+
+        if any_attributes is None:
+            any_attributes = []
+
+        if attributes is None:
+            attributes = {}
+
+        return XmlMeta(
+            clazz=clazz,
+            qname=qname,
+            source_qname=source_qname,
+            nillable=nillable,
+            text=text,
+            choices=choices,
+            elements=elements,
+            wildcards=wildcards,
+            attributes=attributes,
+            any_attributes=any_attributes,
+        )