@@ -125,6 +125,24 @@ pub struct XsdSchema {
125125 /// Used during validation to resolve `QName` type references like
126126 /// `tns:AddressType` to the correct namespace for imported type lookup.
127127 prefix_map : HashMap < String , String > ,
128+ /// The `elementFormDefault` attribute from the schema root.
129+ ///
130+ /// When `Qualified`, local element declarations must be namespace-qualified
131+ /// in instance documents. Default is `Unqualified`.
132+ ///
133+ /// See XSD 1.0 section 3.3.2.
134+ element_form_default : FormDefault ,
135+ }
136+
137+ /// Whether local elements/attributes must be namespace-qualified in instances.
138+ ///
139+ /// See XSD 1.0 section 3.3.2.
140+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
141+ pub enum FormDefault {
142+ /// Local elements do not need to be namespace-qualified (default).
143+ Unqualified ,
144+ /// Local elements must be namespace-qualified in instance documents.
145+ Qualified ,
128146}
129147
130148/// Declarations imported from another namespace via `xsd:import`.
@@ -437,6 +455,10 @@ pub fn parse_xsd_with_options(
437455 column : None ,
438456 } ) ?;
439457 let prefix_map = build_prefix_map ( & root_doc, root_elem) ;
458+ let element_form_default = match root_doc. attribute ( root_elem, "elementFormDefault" ) {
459+ Some ( "qualified" ) => FormDefault :: Qualified ,
460+ _ => FormDefault :: Unqualified ,
461+ } ;
440462
441463 let mut schema = XsdSchema {
442464 target_namespace : None ,
@@ -445,6 +467,7 @@ pub fn parse_xsd_with_options(
445467 attribute_groups : HashMap :: new ( ) ,
446468 imported_namespaces : HashMap :: new ( ) ,
447469 prefix_map,
470+ element_form_default,
448471 } ;
449472
450473 register_builtin_types ( & mut schema) ;
@@ -712,13 +735,18 @@ fn handle_import(
712735 } ;
713736
714737 // We need a temporary XsdSchema to parse into, then extract declarations
738+ let imported_form_default = match imported_doc. attribute ( imported_root, "elementFormDefault" ) {
739+ Some ( "qualified" ) => FormDefault :: Qualified ,
740+ _ => FormDefault :: Unqualified ,
741+ } ;
715742 let mut temp_schema = XsdSchema {
716743 target_namespace : Some ( ns_key. clone ( ) ) ,
717744 elements : HashMap :: new ( ) ,
718745 types : HashMap :: new ( ) ,
719746 attribute_groups : HashMap :: new ( ) ,
720747 imported_namespaces : HashMap :: new ( ) ,
721748 prefix_map : build_prefix_map ( & imported_doc, imported_root) ,
749+ element_form_default : imported_form_default,
722750 } ;
723751 register_builtin_types ( & mut temp_schema) ;
724752 parse_top_level_declarations (
@@ -1410,6 +1438,32 @@ fn validate_sequence(
14101438}
14111439
14121440/// Validates a single element particle in a sequence, returning number consumed.
1441+ /// Checks if an instance element matches a schema element declaration,
1442+ /// accounting for `elementFormDefault` and element-level `form` attributes.
1443+ ///
1444+ /// When qualified form is in effect, the element must have the schema's
1445+ /// target namespace. When unqualified, the element is matched by local
1446+ /// name only (no namespace required).
1447+ fn element_matches_decl (
1448+ doc : & Document ,
1449+ node : NodeId ,
1450+ decl : & XsdElement ,
1451+ schema : & XsdSchema ,
1452+ ) -> bool {
1453+ let child_name = doc. node_name ( node) . unwrap_or ( "" ) ;
1454+ if child_name != decl. name {
1455+ return false ;
1456+ }
1457+ // Check namespace qualification
1458+ if schema. element_form_default == FormDefault :: Qualified {
1459+ if let Some ( ref target_ns) = schema. target_namespace {
1460+ let child_ns = doc. node_namespace ( node) . unwrap_or ( "" ) ;
1461+ return child_ns == target_ns;
1462+ }
1463+ }
1464+ true
1465+ }
1466+
14131467fn validate_sequence_element (
14141468 doc : & Document ,
14151469 children : & [ NodeId ] ,
@@ -1421,8 +1475,7 @@ fn validate_sequence_element(
14211475 let mut count: u32 = 0 ;
14221476 let mut consumed = 0 ;
14231477 for & child in children {
1424- let child_name = doc. node_name ( child) . unwrap_or ( "" ) ;
1425- if child_name != decl. name {
1478+ if !element_matches_decl ( doc, child, decl, schema) {
14261479 break ;
14271480 }
14281481 if let MaxOccurs :: Bounded ( max) = decl. max_occurs {
@@ -1499,7 +1552,7 @@ fn validate_choice(
14991552 let first_name = doc. node_name ( first) . unwrap_or ( "" ) ;
15001553 let matched = particles. iter ( ) . any ( |p| {
15011554 if let XsdParticle :: Element ( decl) = p {
1502- if decl . name == first_name {
1555+ if element_matches_decl ( doc , first , decl , schema ) {
15031556 validate_element ( doc, first, decl, schema, errors) ;
15041557 return true ;
15051558 }
@@ -1536,9 +1589,9 @@ fn validate_all(
15361589 let mut seen: HashMap < & str , u32 > = HashMap :: new ( ) ;
15371590 for & child in children {
15381591 let child_name = doc. node_name ( child) . unwrap_or ( "" ) ;
1539- let matching = particles
1540- . iter ( )
1541- . find ( |p| matches ! ( p , XsdParticle :: Element ( d ) if d . name == child_name ) ) ;
1592+ let matching = particles. iter ( ) . find (
1593+ |p| matches ! ( p , XsdParticle :: Element ( d ) if element_matches_decl ( doc , child , d , schema ) ) ,
1594+ ) ;
15421595 if let Some ( XsdParticle :: Element ( decl) ) = matching {
15431596 let count = seen. entry ( child_name) . or_insert ( 0 ) ;
15441597 * count += 1 ;
@@ -3611,4 +3664,72 @@ mod tests {
36113664 let result2 = validate_xsd ( & doc2, & schema) ;
36123665 assert ! ( !result2. is_valid) ;
36133666 }
3667+
3668+ #[ test]
3669+ fn test_element_form_default_qualified ( ) {
3670+ let schema = parse_xsd (
3671+ r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
3672+ targetNamespace="urn:example"
3673+ xmlns:tns="urn:example"
3674+ elementFormDefault="qualified">
3675+ <xs:element name="order">
3676+ <xs:complexType><xs:sequence>
3677+ <xs:element name="item" type="xs:string"/>
3678+ </xs:sequence></xs:complexType>
3679+ </xs:element>
3680+ </xs:schema>"# ,
3681+ )
3682+ . unwrap ( ) ;
3683+ assert_eq ! ( schema. element_form_default, FormDefault :: Qualified ) ;
3684+
3685+ // Valid: child element is namespace-qualified
3686+ let doc = Document :: parse_str ( r#"<order xmlns="urn:example"><item>Widget</item></order>"# )
3687+ . unwrap ( ) ;
3688+ let result = validate_xsd ( & doc, & schema) ;
3689+ assert ! (
3690+ result. is_valid,
3691+ "qualified children should pass: {:?}" ,
3692+ result. errors
3693+ ) ;
3694+
3695+ // Invalid: child element is NOT namespace-qualified
3696+ let doc_fail = Document :: parse_str (
3697+ r#"<tns:order xmlns:tns="urn:example"><item>Widget</item></tns:order>"# ,
3698+ )
3699+ . unwrap ( ) ;
3700+ let result = validate_xsd ( & doc_fail, & schema) ;
3701+ assert ! (
3702+ !result. is_valid,
3703+ "unqualified child should fail when elementFormDefault=qualified"
3704+ ) ;
3705+ }
3706+
3707+ #[ test]
3708+ fn test_element_form_default_unqualified ( ) {
3709+ let schema = parse_xsd (
3710+ r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
3711+ targetNamespace="urn:example"
3712+ xmlns:tns="urn:example">
3713+ <xs:element name="order">
3714+ <xs:complexType><xs:sequence>
3715+ <xs:element name="item" type="xs:string"/>
3716+ </xs:sequence></xs:complexType>
3717+ </xs:element>
3718+ </xs:schema>"# ,
3719+ )
3720+ . unwrap ( ) ;
3721+ assert_eq ! ( schema. element_form_default, FormDefault :: Unqualified ) ;
3722+
3723+ // Valid: child element without namespace (unqualified is default)
3724+ let doc = Document :: parse_str (
3725+ r#"<tns:order xmlns:tns="urn:example"><item>Widget</item></tns:order>"# ,
3726+ )
3727+ . unwrap ( ) ;
3728+ let result = validate_xsd ( & doc, & schema) ;
3729+ assert ! (
3730+ result. is_valid,
3731+ "unqualified children should pass: {:?}" ,
3732+ result. errors
3733+ ) ;
3734+ }
36143735}
0 commit comments