#[test]
fn test_duplicate_schemas_in_compile_schemas() {
use fastxml::Namespace;
use fastxml::event::{XmlEvent, XmlEventHandler};
use fastxml::schema::validator::OnePassSchemaValidator;
use fastxml::schema::xsd::{compile_schemas, parser::parse_xsd_ast};
use std::sync::Arc;
let gml_xsd = r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:gml="http://www.opengis.net/gml"
targetNamespace="http://www.opengis.net/gml"
elementFormDefault="qualified">
<xs:complexType name="AbstractGMLType" abstract="true">
<xs:sequence>
<xs:element name="description" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="AbstractFeatureType" abstract="true">
<xs:complexContent>
<xs:extension base="gml:AbstractGMLType">
<xs:sequence>
<xs:element name="boundedBy" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>"#;
let core_xsd = r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:core="http://www.opengis.net/citygml/2.0"
xmlns:gml="http://www.opengis.net/gml"
targetNamespace="http://www.opengis.net/citygml/2.0"
elementFormDefault="qualified">
<xs:complexType name="AbstractCityObjectType" abstract="true">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="creationDate" type="xs:date" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>"#;
let tran_xsd = r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tran="http://www.opengis.net/citygml/transportation/2.0"
xmlns:core="http://www.opengis.net/citygml/2.0"
xmlns:gml="http://www.opengis.net/gml"
targetNamespace="http://www.opengis.net/citygml/transportation/2.0"
elementFormDefault="qualified">
<xs:complexType name="TransportationComplexType">
<xs:complexContent>
<xs:extension base="core:AbstractCityObjectType">
<xs:sequence>
<xs:element name="class" type="xs:string" minOccurs="0"/>
<xs:element name="function" type="xs:string" minOccurs="0"/>
<xs:element name="lod1MultiSurface" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="RoadType">
<xs:complexContent>
<xs:extension base="tran:TransportationComplexType">
<xs:sequence>
<xs:element name="trafficArea" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="Road" type="tran:RoadType"/>
</xs:schema>"#;
let gml_ast = parse_xsd_ast(gml_xsd.as_bytes()).unwrap();
let core_ast = parse_xsd_ast(core_xsd.as_bytes()).unwrap();
let tran_ast = parse_xsd_ast(tran_xsd.as_bytes()).unwrap();
let all_schemas = vec![
gml_ast.clone(), core_ast.clone(), tran_ast, gml_ast.clone(), core_ast.clone(), gml_ast.clone(), ];
eprintln!("=== Duplicate schemas test ===");
eprintln!("Number of schemas: {}", all_schemas.len());
let mut compiled = compile_schemas(all_schemas).expect("Failed to compile schemas");
fastxml::schema::xsd::register_builtin_types(&mut compiled);
eprintln!(
"Types in schema: {:?}",
compiled.types.keys().collect::<Vec<_>>()
);
if let Some(flattened) = compiled.type_children_cache.get("RoadType") {
eprintln!(
"RoadType children: {:?}",
flattened.constraints.keys().collect::<Vec<_>>()
);
}
if let Some(flattened) = compiled.type_children_cache.get("tran:RoadType") {
eprintln!(
"tran:RoadType children: {:?}",
flattened.constraints.keys().collect::<Vec<_>>()
);
}
let mut validator = OnePassSchemaValidator::new(Arc::new(compiled));
validator
.handle(&XmlEvent::StartElement {
name: "Road".into(),
prefix: Some("tran".into()),
namespace: Some("http://www.opengis.net/citygml/transportation/2.0".into()),
attributes: vec![],
namespace_decls: vec![
Namespace::new("tran", "http://www.opengis.net/citygml/transportation/2.0"),
Namespace::new("core", "http://www.opengis.net/citygml/2.0"),
Namespace::new("gml", "http://www.opengis.net/gml"),
],
line: Some(1),
column: Some(1),
})
.unwrap();
validator
.handle(&XmlEvent::StartElement {
name: "creationDate".into(),
prefix: Some("core".into()),
namespace: Some("http://www.opengis.net/citygml/2.0".into()),
attributes: vec![],
namespace_decls: vec![],
line: Some(2),
column: Some(1),
})
.unwrap();
validator
.handle(&XmlEvent::Text("2024-01-01".into()))
.unwrap();
validator
.handle(&XmlEvent::EndElement {
name: "creationDate".into(),
prefix: Some("core".into()),
})
.unwrap();
validator
.handle(&XmlEvent::StartElement {
name: "class".into(),
prefix: Some("tran".into()),
namespace: Some("http://www.opengis.net/citygml/transportation/2.0".into()),
attributes: vec![],
namespace_decls: vec![],
line: Some(3),
column: Some(1),
})
.unwrap();
validator.handle(&XmlEvent::Text("9999".into())).unwrap();
validator
.handle(&XmlEvent::EndElement {
name: "class".into(),
prefix: Some("tran".into()),
})
.unwrap();
validator
.handle(&XmlEvent::StartElement {
name: "function".into(),
prefix: Some("tran".into()),
namespace: Some("http://www.opengis.net/citygml/transportation/2.0".into()),
attributes: vec![],
namespace_decls: vec![],
line: Some(4),
column: Some(1),
})
.unwrap();
validator.handle(&XmlEvent::Text("9020".into())).unwrap();
validator
.handle(&XmlEvent::EndElement {
name: "function".into(),
prefix: Some("tran".into()),
})
.unwrap();
validator
.handle(&XmlEvent::StartElement {
name: "lod1MultiSurface".into(),
prefix: Some("tran".into()),
namespace: Some("http://www.opengis.net/citygml/transportation/2.0".into()),
attributes: vec![],
namespace_decls: vec![],
line: Some(5),
column: Some(1),
})
.unwrap();
validator.handle(&XmlEvent::Text("surface".into())).unwrap();
validator
.handle(&XmlEvent::EndElement {
name: "lod1MultiSurface".into(),
prefix: Some("tran".into()),
})
.unwrap();
validator
.handle(&XmlEvent::EndElement {
name: "Road".into(),
prefix: Some("tran".into()),
})
.unwrap();
let errors: Vec<_> = validator
.errors()
.iter()
.filter(|e| e.message.contains("not declared"))
.collect();
eprintln!("Validation errors: {:?}", errors);
assert!(
errors.is_empty(),
"Duplicate schemas should not break inheritance. Errors: {:?}",
errors
);
}
#[test]
fn test_schema_with_default_namespace() {
use fastxml::Namespace;
use fastxml::event::{XmlEvent, XmlEventHandler};
use fastxml::schema::validator::OnePassSchemaValidator;
use fastxml::schema::xsd::{compile_schemas, parser::parse_xsd_ast, register_builtin_types};
use std::sync::Arc;
let gml_xsd = r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:gml="http://www.opengis.net/gml"
targetNamespace="http://www.opengis.net/gml"
elementFormDefault="qualified">
<xs:complexType name="AbstractGMLType" abstract="true">
<xs:sequence>
<xs:element name="description" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="AbstractFeatureType" abstract="true">
<xs:complexContent>
<xs:extension base="gml:AbstractGMLType">
<xs:sequence>
<xs:element name="boundedBy" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>"#;
let core_xsd = r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="http://www.opengis.net/citygml/2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:gml="http://www.opengis.net/gml"
targetNamespace="http://www.opengis.net/citygml/2.0"
elementFormDefault="qualified">
<xs:import namespace="http://www.opengis.net/gml"
schemaLocation="http://schemas.opengis.net/gml/3.1.1/base/gml.xsd"/>
<xs:complexType name="AbstractCityObjectType" abstract="true">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="creationDate" type="xs:date" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="_CityObject" type="AbstractCityObjectType" abstract="true"
substitutionGroup="gml:_Feature"/>
</xs:schema>"#;
let tran_xsd = r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="http://www.opengis.net/citygml/transportation/2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:core="http://www.opengis.net/citygml/2.0"
xmlns:gml="http://www.opengis.net/gml"
targetNamespace="http://www.opengis.net/citygml/transportation/2.0"
elementFormDefault="qualified">
<xs:import namespace="http://www.opengis.net/citygml/2.0"
schemaLocation="http://schemas.opengis.net/citygml/2.0/cityGMLBase.xsd"/>
<xs:import namespace="http://www.opengis.net/gml"
schemaLocation="http://schemas.opengis.net/gml/3.1.1/base/gml.xsd"/>
<xs:complexType name="AbstractTransportationObjectType" abstract="true">
<xs:complexContent>
<xs:extension base="core:AbstractCityObjectType">
<xs:sequence/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="TransportationComplexType">
<xs:complexContent>
<xs:extension base="AbstractTransportationObjectType">
<xs:sequence>
<xs:element name="class" type="xs:string" minOccurs="0"/>
<xs:element name="function" type="xs:string" minOccurs="0"/>
<xs:element name="lod1MultiSurface" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="RoadType">
<xs:complexContent>
<xs:extension base="TransportationComplexType">
<xs:sequence>
<xs:element name="trafficArea" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="Road" type="RoadType" substitutionGroup="core:_CityObject"/>
</xs:schema>"#;
let gml_ast = parse_xsd_ast(gml_xsd.as_bytes()).unwrap();
let core_ast = parse_xsd_ast(core_xsd.as_bytes()).unwrap();
let tran_ast = parse_xsd_ast(tran_xsd.as_bytes()).unwrap();
eprintln!("=== Default namespace test ===");
eprintln!("gml namespace_bindings: {:?}", gml_ast.namespace_bindings);
eprintln!("core namespace_bindings: {:?}", core_ast.namespace_bindings);
eprintln!("tran namespace_bindings: {:?}", tran_ast.namespace_bindings);
let all_schemas = vec![gml_ast, core_ast, tran_ast];
let mut compiled = compile_schemas(all_schemas).expect("Failed to compile schemas");
register_builtin_types(&mut compiled);
eprintln!("Types in schema:");
for type_name in compiled.types.keys() {
if type_name.contains("Road")
|| type_name.contains("Transportation")
|| type_name.contains("CityObject")
{
eprintln!(" {}", type_name);
}
}
eprintln!("Type children cache:");
for (type_name, flattened) in &compiled.type_children_cache {
if type_name.contains("Road") {
eprintln!(
" {}: {:?}",
type_name,
flattened.constraints.keys().collect::<Vec<_>>()
);
}
}
eprintln!("Elements:");
for (elem_name, elem_def) in &compiled.elements {
if elem_name.contains("Road") {
eprintln!(" {} -> type_ref: {:?}", elem_name, elem_def.type_ref);
}
}
let mut validator = OnePassSchemaValidator::new(Arc::new(compiled));
validator
.handle(&XmlEvent::StartElement {
name: "Road".into(),
prefix: Some("tran".into()),
namespace: Some("http://www.opengis.net/citygml/transportation/2.0".into()),
attributes: vec![],
namespace_decls: vec![
Namespace::new("tran", "http://www.opengis.net/citygml/transportation/2.0"),
Namespace::new("core", "http://www.opengis.net/citygml/2.0"),
Namespace::new("gml", "http://www.opengis.net/gml"),
],
line: Some(1),
column: Some(1),
})
.unwrap();
validator
.handle(&XmlEvent::StartElement {
name: "creationDate".into(),
prefix: Some("core".into()),
namespace: Some("http://www.opengis.net/citygml/2.0".into()),
attributes: vec![],
namespace_decls: vec![],
line: Some(2),
column: Some(1),
})
.unwrap();
validator
.handle(&XmlEvent::Text("2024-01-01".into()))
.unwrap();
validator
.handle(&XmlEvent::EndElement {
name: "creationDate".into(),
prefix: Some("core".into()),
})
.unwrap();
validator
.handle(&XmlEvent::StartElement {
name: "class".into(),
prefix: Some("tran".into()),
namespace: Some("http://www.opengis.net/citygml/transportation/2.0".into()),
attributes: vec![],
namespace_decls: vec![],
line: Some(3),
column: Some(1),
})
.unwrap();
validator.handle(&XmlEvent::Text("9999".into())).unwrap();
validator
.handle(&XmlEvent::EndElement {
name: "class".into(),
prefix: Some("tran".into()),
})
.unwrap();
validator
.handle(&XmlEvent::StartElement {
name: "function".into(),
prefix: Some("tran".into()),
namespace: Some("http://www.opengis.net/citygml/transportation/2.0".into()),
attributes: vec![],
namespace_decls: vec![],
line: Some(4),
column: Some(1),
})
.unwrap();
validator.handle(&XmlEvent::Text("9020".into())).unwrap();
validator
.handle(&XmlEvent::EndElement {
name: "function".into(),
prefix: Some("tran".into()),
})
.unwrap();
validator
.handle(&XmlEvent::StartElement {
name: "lod1MultiSurface".into(),
prefix: Some("tran".into()),
namespace: Some("http://www.opengis.net/citygml/transportation/2.0".into()),
attributes: vec![],
namespace_decls: vec![],
line: Some(5),
column: Some(1),
})
.unwrap();
validator.handle(&XmlEvent::Text("surface".into())).unwrap();
validator
.handle(&XmlEvent::EndElement {
name: "lod1MultiSurface".into(),
prefix: Some("tran".into()),
})
.unwrap();
validator
.handle(&XmlEvent::EndElement {
name: "Road".into(),
prefix: Some("tran".into()),
})
.unwrap();
let errors: Vec<_> = validator
.errors()
.iter()
.filter(|e| e.message.contains("not declared"))
.collect();
eprintln!("Validation errors: {:?}", errors);
assert!(
errors.is_empty(),
"Schema with default namespace should work. Errors: {:?}",
errors
);
}
#[test]
fn test_duplicate_schemas_deduplication() {
use fastxml::schema::xsd::{compile_schemas, parser::parse_xsd_ast, register_builtin_types};
let gml_xsd = r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:gml="http://www.opengis.net/gml"
targetNamespace="http://www.opengis.net/gml"
elementFormDefault="qualified">
<xs:complexType name="AbstractFeatureType" abstract="true">
<xs:sequence>
<xs:element name="boundedBy" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:schema>"#;
let core_xsd = r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="http://www.opengis.net/citygml/2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:gml="http://www.opengis.net/gml"
targetNamespace="http://www.opengis.net/citygml/2.0"
elementFormDefault="qualified">
<xs:complexType name="AbstractCityObjectType" abstract="true">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="creationDate" type="xs:date" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>"#;
let tran_xsd = r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="http://www.opengis.net/citygml/transportation/2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:core="http://www.opengis.net/citygml/2.0"
xmlns:gml="http://www.opengis.net/gml"
targetNamespace="http://www.opengis.net/citygml/transportation/2.0"
elementFormDefault="qualified">
<xs:complexType name="TransportationComplexType">
<xs:complexContent>
<xs:extension base="core:AbstractCityObjectType">
<xs:sequence>
<xs:element name="class" type="xs:string" minOccurs="0"/>
<xs:element name="function" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="RoadType">
<xs:complexContent>
<xs:extension base="TransportationComplexType">
<xs:sequence>
<xs:element name="trafficArea" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="Road" type="RoadType"/>
</xs:schema>"#;
let gml_ast = parse_xsd_ast(gml_xsd.as_bytes()).unwrap();
let core_ast = parse_xsd_ast(core_xsd.as_bytes()).unwrap();
let tran_ast = parse_xsd_ast(tran_xsd.as_bytes()).unwrap();
let all_schemas = vec![
gml_ast.clone(),
core_ast.clone(),
tran_ast.clone(),
gml_ast.clone(),
core_ast.clone(),
gml_ast.clone(),
gml_ast.clone(),
core_ast.clone(),
gml_ast,
core_ast,
tran_ast,
];
eprintln!("=== Duplicate schemas deduplication test ===");
eprintln!("Input schemas count: {}", all_schemas.len());
let mut compiled = compile_schemas(all_schemas).expect("Failed to compile schemas");
register_builtin_types(&mut compiled);
let road_type_cache = compiled.type_children_cache.get("RoadType");
eprintln!(
"RoadType cache: {:?}",
road_type_cache.map(|f| f.constraints.keys().collect::<Vec<_>>())
);
let road_type_cache = road_type_cache.expect("RoadType should be in cache");
assert!(
road_type_cache.constraints.contains_key("trafficArea"),
"RoadType should have trafficArea"
);
assert!(
road_type_cache.constraints.contains_key("class"),
"RoadType should have class (inherited from TransportationComplexType)"
);
assert!(
road_type_cache.constraints.contains_key("function"),
"RoadType should have function (inherited from TransportationComplexType)"
);
assert!(
road_type_cache.constraints.contains_key("creationDate"),
"RoadType should have creationDate (inherited from AbstractCityObjectType)"
);
assert!(
road_type_cache.constraints.contains_key("boundedBy"),
"RoadType should have boundedBy (inherited from AbstractFeatureType)"
);
let double_prefix_keys: Vec<_> = compiled
.type_children_cache
.keys()
.filter(|k| k.matches(':').count() >= 2)
.collect();
assert!(
double_prefix_keys.is_empty(),
"Should not have double-prefix keys in type_children_cache, found: {:?}",
double_prefix_keys
);
assert!(
compiled
.type_children_cache
.contains_key("TransportationComplexType"),
"Should have TransportationComplexType in cache"
);
assert!(
compiled
.type_children_cache
.contains_key("AbstractCityObjectType"),
"Should have AbstractCityObjectType in cache"
);
assert!(
compiled
.type_children_cache
.contains_key("AbstractFeatureType"),
"Should have AbstractFeatureType in cache"
);
}