Repository: tefra/xsdata
Pull request #527 (2021-06-14 19:08:23)

JSON Parser: try all someclasses for element nodes

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


codecov[bot]: # [Codecov](https://codecov.io/gh/tefra/xsdata/pull/527?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) Report
> Merging [#527](https://codecov.io/gh/tefra/xsdata/pull/527?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) (d7fab05) into [master](https://codecov.io/gh/tefra/xsdata/commit/54ebc766744f39ab0afa6fafee6bfb1cf5cc584f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) (54ebc76) will **not change** coverage.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/tefra/xsdata/pull/527/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/527?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      #527   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           95        95           
  Lines         7658      7662    +4     
  Branches      1324      1325    +1     
=========================================
+ Hits          7658      7662    +4     
```


| [Impacted Files](https://codecov.io/gh/tefra/xsdata/pull/527?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/parsers/json.py](https://codecov.io/gh/tefra/xsdata/pull/527/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%> (ø)` | |

------

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


Patch:
diff --git a/xsdata/formats/dataclass/parsers/json.py b/xsdata/formats/dataclass/parsers/json.py
index 60be7154..a598b1ff 100644
--- a/xsdata/formats/dataclass/parsers/json.py
+++ b/xsdata/formats/dataclass/parsers/json.py
@@ -258,6 +258,13 @@ class JsonParser(AbstractParser):
             return self.bind_best_dataclass(data, meta.element_types)
         else:
             assert var.clazz is not None
+
+            subclasses = set(self.context.get_subclasses(var.clazz))
+            if subclasses:
+                # field annotation is an abstract/base type
+                subclasses.add(var.clazz)
+                return self.bind_best_dataclass(data, subclasses)
+
             return self.bind_dataclass(data, var.clazz)
 
     def bind_derived_value(self, meta: XmlMeta, var: XmlVar, data: Dict) -> T:


Test patch:
diff --git a/tests/fixtures/models.py b/tests/fixtures/models.py
index 6a61fb62..41d02018 100644
--- a/tests/fixtures/models.py
+++ b/tests/fixtures/models.py
@@ -40,6 +40,21 @@ class TypeDuplicate:
     x1: str = field(metadata={"name": "x"})
 
 
+@dataclass
+class BaseA:
+    x: str
+
+
+@dataclass
+class BaseB(BaseA):
+    y: str
+
+
+@dataclass
+class BaseC(BaseB):
+    z: str
+
+
 @dataclass
 class ExtendedType:
     a: Optional[TypeA] = field(default=None)
@@ -75,6 +90,11 @@ class UnionType:
     element: Union[TypeA, TypeB, TypeC, TypeD]
 
 
+@dataclass
+class BaseType:
+    element: BaseA
+
+
 @dataclass
 class AttrsType:
     index: int = field(metadata={"type": "Attribute"})
diff --git a/tests/formats/dataclass/parsers/test_json.py b/tests/formats/dataclass/parsers/test_json.py
index d134d4ec..bb8ef91f 100644
--- a/tests/formats/dataclass/parsers/test_json.py
+++ b/tests/formats/dataclass/parsers/test_json.py
@@ -7,6 +7,8 @@ from tests import fixtures_dir
 from tests.fixtures.books import BookForm
 from tests.fixtures.books import Books
 from tests.fixtures.models import AttrsType
+from tests.fixtures.models import BaseC
+from tests.fixtures.models import BaseType
 from tests.fixtures.models import ChoiceType
 from tests.fixtures.models import ExtendedType
 from tests.fixtures.models import TypeA
@@ -223,6 +225,12 @@ class JsonParserTests(FactoryTestCase):
             str(cm.exception),
         )
 
+    def test_bind_dataclass_subclasses(self):
+        data = {"element": {"x": "1", "y": "foo", "z": "1.0"}}
+        actual = self.parser.bind_dataclass(data, BaseType)
+
+        self.assertIsInstance(actual.element, BaseC)
+
     def test_bind_attributes(self):
         data = {"attrs": {"a": 1, "b": 2}, "index": 1}