Repository: tefra/xsdata

Issue:
xsi:attributes break parsing.
If I have some XML which declares its own schema, which is useful sometimes, the XML gets unparsable.

E.g.:
```python
def test_tolerate_xsi(self):
       xml =  """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- This clip does not have a mid. So it will be a new clip when you post this -->
<program xmlns="urn:vpro:media:update:2009"
         type="CLIP" avType="VIDEO"
         publishStart="2012-01-11T16:16:01.287+01:00" embeddable="true"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="urn:vpro:media:update:2009 https://poms.omroep.nl/schema/update/vproMediaUpdate.xsd"        
         >
  <broadcaster>VPRO</broadcaster>
  <title type="MAIN">TEST TEST</title>
  <tag>Kabouters</tag>
  <ageRating>ALL</ageRating>
  <email>programma@avro.nl</email>  
</program>         
         """
       program_update =  parser.from_string(xml)
       self.assertEqual(program_update.ageRating, AgeRatingType.ALL)
``` 
The parser step fails with the following error:
```

self = <xsdata.formats.dataclass.parsers.nodes.element.ElementNode object at 0x12285a670>
params = {'avType': <AvTypeEnum.VIDEO: 'VIDEO'>, 'embeddable': True, 'publishStart': XmlDateTime(2012, 1, 11, 16, 16, 1, 287000000, 60), 'typeValue': <ProgramTypeEnum.CLIP: 'CLIP'>}

    def bind_attrs(self, params: Dict):
        """Parse the given element's attributes and any text content and return
        a dictionary of field names and values based on the given class
        metadata."""
    
        if not self.attrs:
            return
    
        for qname, value in self.attrs.items():
            var = self.meta.find_attribute(qname)
            if var and var.name not in params:
                self.bind_attr(params, var, value)
            else:
                var = self.meta.find_any_attributes(qname)
                if var:
                    self.bind_any_attr(params, var, qname, value)
                else:
                    if self.config.fail_on_unknown_attributes:
>                       raise ParserError(
                            f"Unknown attribute {self.meta.qname}:{qname}"
                        )
E                       xsdata.exceptions.ParserError: Unknown attribute {urn:vpro:media:update:2009}program:{http://www.w3.org/2001/XMLSchema-instance}schemaLocation

/Users/michiel/venvs/pyapi-env/lib/python3.11/site-packages/xsdata/formats/dataclass/parsers/nodes/element.py:138: ParserError

Process finished with exit code 1
```

I think this is erroneous. The attribute schemaLocation is not unknown, it is specified by xmlns:xsi. I think it can just be ignored, even if fail_on_unknown_attributes=True.




Discussion:
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=tefra_xsdata&pullRequest=846)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=846&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=846&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=846&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=846&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=846&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=846&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=846&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=846&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=846&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=846&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=846&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=846&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=846) No Coverage information  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=846&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=846&metric=new_duplicated_lines_density&view=list)


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/tefra/xsdata/pull/846?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) Report
Patch coverage: **`100.00%`** and project coverage change: **`-0.26%`** :warning:
> Comparison is base [(`31f672a`)](https://app.codecov.io/gh/tefra/xsdata/commit/31f672af84fd040a97996871916a41b1046fe46b?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) 99.89% compared to head [(`625a650`)](https://app.codecov.io/gh/tefra/xsdata/pull/846?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) 99.63%.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #846      +/-   ##
==========================================
- Coverage   99.89%   99.63%   -0.26%     
==========================================
  Files         104      104              
  Lines        9282     9284       +2     
  Branches     2076     2076              
==========================================
- Hits         9272     9250      -22     
- Misses          3       23      +20     
- Partials        7       11       +4     
```


| [Files Changed](https://app.codecov.io/gh/tefra/xsdata/pull/846?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/nodes/element.py](https://app.codecov.io/gh/tefra/xsdata/pull/846?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou#diff-eHNkYXRhL2Zvcm1hdHMvZGF0YWNsYXNzL3BhcnNlcnMvbm9kZXMvZWxlbWVudC5weQ==) | `99.04% <100.00%> (+<0.01%)` | :arrow_up: |

... and [2 files with indirect coverage changes](https://app.codecov.io/gh/tefra/xsdata/pull/846/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou)


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/tefra/xsdata/pull/846?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou).


Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 728361fb..cca18ba0 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -41,7 +41,7 @@ repos:
       - id: docformatter
         args: [ "--in-place", "--pre-summary-newline" ]
   - repo: https://github.com/pre-commit/mirrors-mypy
-    rev: v1.5.0
+    rev: v1.5.1
     hooks:
       - id: mypy
         files: ^(xsdata/)
diff --git a/xsdata/formats/dataclass/parsers/nodes/element.py b/xsdata/formats/dataclass/parsers/nodes/element.py
index 41bc22ae..46bfd97a 100644
--- a/xsdata/formats/dataclass/parsers/nodes/element.py
+++ b/xsdata/formats/dataclass/parsers/nodes/element.py
@@ -17,6 +17,8 @@ from xsdata.formats.dataclass.parsers.utils import ParserUtils
 from xsdata.formats.dataclass.parsers.utils import PendingCollection
 from xsdata.logger import logger
 from xsdata.models.enums import DataType
+from xsdata.models.enums import Namespace
+from xsdata.utils.namespaces import target_uri
 
 
 class ElementNode(XmlNode):
@@ -134,7 +136,10 @@ class ElementNode(XmlNode):
                 if var:
                     self.bind_any_attr(params, var, qname, value)
                 else:
-                    if self.config.fail_on_unknown_attributes:
+                    if (
+                        self.config.fail_on_unknown_attributes
+                        and target_uri(qname) != Namespace.XSI.uri
+                    ):
                         raise ParserError(
                             f"Unknown attribute {self.meta.qname}:{qname}"
                         )


Test patch:
diff --git a/tests/formats/dataclass/parsers/nodes/test_element.py b/tests/formats/dataclass/parsers/nodes/test_element.py
index 878c5e7a..66cd9968 100644
--- a/tests/formats/dataclass/parsers/nodes/test_element.py
+++ b/tests/formats/dataclass/parsers/nodes/test_element.py
@@ -221,6 +221,15 @@ class ElementNodeTests(FactoryTestCase):
 
         self.assertEqual("Unknown attribute ExtendedType:a", str(cm.exception))
 
+    def test_bind_with_fail_on_unknown_attributes_ignores_xsi_attributes(self):
+        self.node.meta = self.context.build(ExtendedType)
+        self.node.config.fail_on_unknown_attributes = True
+        self.node.attrs = {QNames.XSI_TYPE: "b"}
+
+        objects = []
+        self.node.bind("foo", "text", "tail", objects)
+        self.assertEqual(1, len(objects))
+
     @mock.patch("xsdata.formats.dataclass.parsers.nodes.element.logger.warning")
     def test_bind_objects(self, mock_warning):
         self.node.meta = self.context.build(TypeC)