Repository: tefra/xsdata
Pull request #291 (2020-10-10 16:40:21)

UnionNode:score_object weight non str values higher

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


codecov[bot]: # [Codecov](https://codecov.io/gh/tefra/xsdata/pull/291?src=pr&el=h1) Report
> Merging [#291](https://codecov.io/gh/tefra/xsdata/pull/291?src=pr&el=desc) into [master](https://codecov.io/gh/tefra/xsdata/commit/2ed65acfeea3c5271930646835157cf245ea7282?el=desc) will **not change** coverage.
> The diff coverage is `100.00%`.

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

```diff
@@            Coverage Diff            @@
##            master      #291   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           69        69           
  Lines         5276      5246   -30     
  Branches       902       905    +3     
=========================================
- Hits          5276      5246   -30     
```


| [Impacted Files](https://codecov.io/gh/tefra/xsdata/pull/291?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [xsdata/formats/dataclass/parsers/nodes.py](https://codecov.io/gh/tefra/xsdata/pull/291/diff?src=pr&el=tree#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL3BhcnNlcnMvbm9kZXMucHk=) | `100.00% <100.00%> (ø)` | |
| [xsdata/cli.py](https://codecov.io/gh/tefra/xsdata/pull/291/diff?src=pr&el=tree#diff-eHNkYXRhL2NsaS5weQ==) | `100.00% <0.00%> (ø)` | |
| [xsdata/models/xsd.py](https://codecov.io/gh/tefra/xsdata/pull/291/diff?src=pr&el=tree#diff-eHNkYXRhL21vZGVscy94c2QucHk=) | `100.00% <0.00%> (ø)` | |
| [xsdata/models/wsdl.py](https://codecov.io/gh/tefra/xsdata/pull/291/diff?src=pr&el=tree#diff-eHNkYXRhL21vZGVscy93c2RsLnB5) | `100.00% <0.00%> (ø)` | |
| [xsdata/models/config.py](https://codecov.io/gh/tefra/xsdata/pull/291/diff?src=pr&el=tree#diff-eHNkYXRhL21vZGVscy9jb25maWcucHk=) | `100.00% <0.00%> (ø)` | |
| [xsdata/models/mixins.py](https://codecov.io/gh/tefra/xsdata/pull/291/diff?src=pr&el=tree#diff-eHNkYXRhL21vZGVscy9taXhpbnMucHk=) | `100.00% <0.00%> (ø)` | |
| [xsdata/codegen/models.py](https://codecov.io/gh/tefra/xsdata/pull/291/diff?src=pr&el=tree#diff-eHNkYXRhL2NvZGVnZW4vbW9kZWxzLnB5) | `100.00% <0.00%> (ø)` | |
| [xsdata/codegen/writer.py](https://codecov.io/gh/tefra/xsdata/pull/291/diff?src=pr&el=tree#diff-eHNkYXRhL2NvZGVnZW4vd3JpdGVyLnB5) | `100.00% <0.00%> (ø)` | |
| [xsdata/formats/mixins.py](https://codecov.io/gh/tefra/xsdata/pull/291/diff?src=pr&el=tree#diff-eHNkYXRhL2Zvcm1hdHMvbWl4aW5zLnB5) | `100.00% <0.00%> (ø)` | |
| [xsdata/codegen/resolver.py](https://codecov.io/gh/tefra/xsdata/pull/291/diff?src=pr&el=tree#diff-eHNkYXRhL2NvZGVnZW4vcmVzb2x2ZXIucHk=) | `100.00% <0.00%> (ø)` | |
| ... and [13 more](https://codecov.io/gh/tefra/xsdata/pull/291/diff?src=pr&el=tree-more) | |

------

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

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

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



Patch:
diff --git a/xsdata/formats/dataclass/parsers/nodes.py b/xsdata/formats/dataclass/parsers/nodes.py
index 06b6bc35..7254f3a5 100644
--- a/xsdata/formats/dataclass/parsers/nodes.py
+++ b/xsdata/formats/dataclass/parsers/nodes.py
@@ -250,7 +250,7 @@ class UnionNode(XmlNode):
         self.events.insert(0, ("start", qname, copy.deepcopy(self.attrs), self.ns_map))
 
         obj = None
-        max_score = -1
+        max_score = -1.0
         for clazz in self.var.types:
             candidate = self.parse_class(clazz)
             score = self.score_object(candidate)
@@ -274,13 +274,28 @@ class UnionNode(XmlNode):
             return None
 
     @classmethod
-    def score_object(cls, obj: Any) -> int:
-        """Sum all not None field values for the given object."""
-        return (
-            sum(1 for var in fields(obj) if getattr(obj, var.name) is not None)
-            if obj
-            else -1
-        )
+    def score_object(cls, obj: Any) -> float:
+        """
+        Score a dataclass instance by the field values types.
+
+        Weights:
+            1. None: 0
+            2. str: 1
+            3. *: 1.5
+        """
+
+        if not obj:
+            return -1.0
+
+        score = 0.0
+        for var in fields(obj):
+            val = getattr(obj, var.name)
+            if isinstance(val, str):
+                score += 1.0
+            elif val is not None:
+                score += 1.5
+
+        return score
 
 
 @dataclass


Test patch:
diff --git a/tests/formats/dataclass/parsers/test_nodes.py b/tests/formats/dataclass/parsers/test_nodes.py
index b9cd1559..a1fea466 100644
--- a/tests/formats/dataclass/parsers/test_nodes.py
+++ b/tests/formats/dataclass/parsers/test_nodes.py
@@ -450,6 +450,12 @@ class UnionNodeTests(TestCase):
 
         self.assertEqual("Failed to parse union node: item", str(cm.exception))
 
+    def test_score_object(self):
+        self.assertEqual(-1.0, UnionNode.score_object(None))
+
+        cls = make_dataclass("b", [("x", int), ("y", str), ("z", Any)])
+        self.assertEqual(2.5, UnionNode.score_object(cls(1, "1", None)))
+
 
 class PrimitiveNodeTests(TestCase):
     @mock.patch.object(ParserUtils, "parse_value")