Repository: tefra/xsdata

Issue:
Support xs:anyType root elements
```xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">			
  <xs:complexType name="t">
	<xs:attribute ref="xml:blah"/>
  </xs:complexType>
</xs:schema>
```

```xml
<doc xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="t" xml:blah="a"/>
```

Doc is not defined anywhere

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


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

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

```diff
@@            Coverage Diff            @@
##            master      #400   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           72        72           
  Lines         6474      6476    +2     
  Branches      1139      1140    +1     
=========================================
+ Hits          6474      6476    +2     
```


| [Impacted Files](https://codecov.io/gh/tefra/xsdata/pull/400?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [xsdata/formats/dataclass/parsers/nodes.py](https://codecov.io/gh/tefra/xsdata/pull/400/diff?src=pr&el=tree#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL3BhcnNlcnMvbm9kZXMucHk=) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tefra/xsdata/pull/400?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/400?src=pr&el=footer). Last update [f992655...c093872](https://codecov.io/gh/tefra/xsdata/pull/400?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/xsdata/formats/dataclass/parsers/nodes.py b/xsdata/formats/dataclass/parsers/nodes.py
index 6d2c12fb..3ae59922 100644
--- a/xsdata/formats/dataclass/parsers/nodes.py
+++ b/xsdata/formats/dataclass/parsers/nodes.py
@@ -463,14 +463,20 @@ class NodeParser(PushParser):
             item = queue[-1]
             child = item.child(qname, attrs, ns_map, len(objects))
         except IndexError:
+            xsi_type = ParserUtils.xsi_type(attrs, ns_map)
 
+            # Match element qname directly
             if clazz is None:
                 clazz = self.context.find_type(qname)
 
+            # Root is xs:anyType try xsi:type
+            if clazz is None and xsi_type:
+                clazz = self.context.find_type(xsi_type)
+
+            # Exit if we still have no binding model
             if clazz is None:
                 raise ParserError(f"No class found matching root: {qname}")
 
-            xsi_type = ParserUtils.xsi_type(attrs, ns_map)
             meta = self.context.fetch(clazz, xsi_type=xsi_type)
             derived = xsi_type is not None and meta.qname != qname
 


Test patch:
diff --git a/tests/formats/dataclass/parsers/test_nodes.py b/tests/formats/dataclass/parsers/test_nodes.py
index d73c5d53..20b2a5c3 100644
--- a/tests/formats/dataclass/parsers/test_nodes.py
+++ b/tests/formats/dataclass/parsers/test_nodes.py
@@ -752,12 +752,32 @@ class NodeParserTests(TestCase):
         self.assertEqual(expected_node, queue[0])
 
         with self.assertRaises(ParserError) as cm:
-            parser.start(None, [], [], "{unknown}hopefully", [], {})
+            parser.start(None, [], [], "{unknown}hopefully", {}, {})
 
         self.assertEqual(
             "No class found matching root: {unknown}hopefully", str(cm.exception)
         )
 
+    def test_start_with_any_type_root(self):
+        parser = self.parser
+        queue = []
+        objects = []
+
+        attrs = {QNames.XSI_TYPE: "bk:books"}
+        ns_map = {"bk": "urn:books", "xsi": Namespace.XSI.uri}
+        expected_node = ElementNode(
+            position=0,
+            context=parser.context,
+            meta=parser.context.build(Books),
+            config=parser.config,
+            attrs=attrs,
+            ns_map=ns_map,
+            derived=True,
+        )
+        parser.start(None, queue, objects, "doc", attrs, ns_map)
+        self.assertEqual(1, len(queue))
+        self.assertEqual(expected_node, queue[0])
+
     def test_start_with_derived_class(self):
         a = make_dataclass("a", fields=[])
         b = make_dataclass("b", fields=[], bases=(a,))