from dataclasses import dataclass
from dataclasses import field
from pathlib import Path
from typing import Any as Anything
from typing import Dict
from typing import Iterator
from typing import List as Array
from typing import Optional
from typing import Union as UnionType
from xsdata.exceptions import SchemaValueError
from xsdata.formats.dataclass.serializers import XmlSerializer
from xsdata.models.enums import DataType
from xsdata.models.enums import FormType
from xsdata.models.enums import Mode
from xsdata.models.enums import Namespace
from xsdata.models.enums import NamespaceType
from xsdata.models.enums import ProcessType
from xsdata.models.enums import TagType
from xsdata.models.enums import UseType
from xsdata.models.mixins import ElementBase
from xsdata.utils.text import collapse_whitespace
[docs]def attribute(default=None, init=True, **kwargs):
kwargs.update(type=TagType.ATTRIBUTE)
return field(init=init, default=default, metadata=kwargs)
[docs]def element(init=True, **kwargs):
kwargs.update(type=TagType.ELEMENT)
return field(init=init, default=None, metadata=kwargs)
[docs]def array_element(init=True, **kwargs):
kwargs.update(type=TagType.ELEMENT)
return field(init=init, default_factory=list, metadata=kwargs)
[docs]def array_any_element(init=True, **kwargs):
kwargs.update(type=TagType.ANY, namespace=NamespaceType.ANY.value)
return field(init=init, default_factory=list, metadata=kwargs)
[docs]@dataclass(frozen=True)
class XmlString:
elements: Array[object] = array_any_element()
[docs] def render(self):
name = self.__class__.__name__
xml = XmlSerializer(pretty_print=True, xml_declaration=False).render(self)
return xml[xml.find(">") + 1 :].replace(f"</{name}>", "").strip()
[docs]@dataclass
class Documentation(ElementBase):
"""
<documentation
source = anyURI
xml:lang = language
{any attributes with non-schema namespace . . .}>
Content: ({any})*
</documentation>
"""
lang: Optional[str] = attribute()
source: Optional[str] = attribute()
elements: Array[object] = array_any_element()
attributes: Optional["AnyAttribute"] = element()
[docs] def tostring(self) -> Optional[str]:
if self.elements:
return XmlString(self.elements).render()
else:
return None
[docs]@dataclass
class Appinfo(ElementBase):
"""
<appinfo
source = anyURI
{any attributes with non-schema namespace . . .}>
Content: ({any})*
</appinfo>
"""
source: Optional[str] = attribute()
elements: Array[object] = array_any_element()
any_attribute: Optional["AnyAttribute"] = element()
[docs]@dataclass
class Annotation(ElementBase):
"""
<annotation
id = ID
{any attributes with non-schema namespace . . .}>
Content: (appinfo | documentation)*
</annotation>
"""
appinfo: Optional[Appinfo] = element()
documentations: Array[Documentation] = array_element(name="documentation")
any_attribute: Optional["AnyAttribute"] = element()
[docs]@dataclass
class AnnotationBase(ElementBase):
"""Base Class for elements that can contain annotations."""
annotation: Optional[Annotation] = element()
any_attribute: Optional["AnyAttribute"] = element()
@property
def display_help(self) -> Optional[str]:
if self.annotation and len(self.annotation.documentations):
return "\n".join(
filter(None, [doc.tostring() for doc in self.annotation.documentations])
)
return None
[docs]@dataclass
class AnyAttribute(AnnotationBase):
"""
<anyAttribute
id = ID
namespace = ((##any | ##other) | List of (anyURI | (##targetNamespace | ##local)))
notNamespace = List of (anyURI | (##targetNamespace | ##local))
notQName = List of (QName | ##defined)
processContents = (lax | skip | strict) : strict
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</anyAttribute>
"""
namespace: Optional[str] = attribute(default="##any")
process_contents: Optional[ProcessType] = attribute()
def __post_init__(self):
self.namespace = collapse_whitespace(self.namespace)
@property
def is_attribute(self) -> bool:
return True
@property
def is_wildcard(self) -> bool:
return True
@property
def raw_namespace(self) -> Optional[str]:
return self.namespace
@property
def real_name(self) -> str:
return f"{self.namespace}_attributes"
@property
def real_type(self) -> Optional[str]:
prefix = self.schema_prefix()
suffix = DataType.QMAP.code
return f"{prefix}:{suffix}" if prefix else suffix
[docs]@dataclass
class Assertion(AnnotationBase):
"""
<assertion
id = ID
test = an XPath expression
xpathDefaultNamespace =
(anyURI | (##defaultNamespace | ##targetNamespace | ##local))
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</assertion>
"""
test: Optional[str] = attribute()
[docs]@dataclass
class SimpleType(AnnotationBase):
"""
<simpleType
final = (#all | List of (list | union | restriction | extension))
id = ID
name = NCName
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (restriction | list | union))
</simpleType>
"""
name: Optional[str] = attribute()
restriction: Optional["Restriction"] = element()
list: Optional["List"] = element()
union: Optional["Union"] = element()
@property
def is_enumeration(self):
return self.restriction and len(self.restriction.enumerations) > 0
@property
def is_attribute(self) -> bool:
return self.is_enumeration
@property
def real_type(self) -> Optional[str]:
if self.restriction:
return self.restriction.real_type
if self.list:
return self.list.real_type
if self.union:
return self.union.member_types
return None
[docs] def get_restrictions(self) -> Dict[str, Anything]:
if self.restriction:
return self.restriction.get_restrictions()
if self.list:
return self.list.get_restrictions()
return dict()
[docs]@dataclass
class List(AnnotationBase):
"""
<list
id = ID
itemType = QName
{any attributes with non-schema namespace . . .}>
Content: (annotation?, simpleType?)
</list>
"""
simple_type: Optional[SimpleType] = element()
item_type: Optional[str] = attribute()
@property
def is_attribute(self) -> bool:
return True
@property
def real_name(self) -> str:
return "value"
@property
def real_type(self) -> Optional[str]:
return None
[docs]@dataclass
class Union(AnnotationBase):
"""
<union
id = ID
memberTypes = List of QName
{any attributes with non-schema namespace . . .}>
Content: (annotation?, simpleType*)
</union>
"""
member_types: Optional[str] = attribute()
simple_types: Array[SimpleType] = array_element(name="simpleType")
@property
def extends(self) -> Optional[str]:
if self.member_types:
return self.member_types
return None
@property
def is_attribute(self) -> bool:
return True
@property
def real_type(self) -> Optional[str]:
types = []
if self.simple_types:
types.extend(
[
simple_type.real_type
for simple_type in self.simple_types
if simple_type.real_type
]
)
if self.member_types:
types.extend([member for member in self.member_types.split(" ") if member])
return " ".join(types) if types else None
@property
def real_name(self) -> str:
return "value"
[docs] def get_restrictions(self) -> Dict[str, Anything]:
restrictions = dict()
for simple_type in self.simple_types:
restrictions.update(simple_type.get_restrictions())
return restrictions
[docs]@dataclass
class Attribute(AnnotationBase):
"""
<attribute
default = string
fixed = string
form = (qualified | unqualified)
id = ID
name = NCName
ref = QName
targetNamespace = anyURI
type = QName
use = (optional | prohibited | required) : optional
inheritable = boolean
{any attributes with non-schema namespace . . .}>
Content: (annotation?, simpleType?)
</attribute>
"""
default: Optional[str] = attribute()
fixed: Optional[str] = attribute()
form: Optional[FormType] = attribute()
name: Optional[str] = attribute()
ref: Optional[str] = attribute()
type: Optional[str] = attribute()
target_namespace: Optional[str] = attribute(name="targetNamespace")
simple_type: Optional[SimpleType] = element()
use: Optional[UseType] = attribute(default=UseType.OPTIONAL)
@property
def is_attribute(self) -> bool:
return True
@property
def real_type(self) -> Optional[str]:
if self.simple_type:
return self.simple_type.real_type
if self.type:
return self.type
if self.ref:
return self.ref
return None
[docs] def get_restrictions(self) -> Dict[str, Anything]:
restrictions = dict()
if self.use == UseType.REQUIRED:
restrictions.update({"min_occurs": 1, "max_occurs": 1, "required": True})
elif self.use == UseType.PROHIBITED:
restrictions.update({"prohibited": True})
if self.simple_type:
restrictions.update(self.simple_type.get_restrictions())
return restrictions
[docs]@dataclass
class AttributeGroup(AnnotationBase):
"""
<attributeGroup
id = ID
ref = QName
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</attributeGroup>
"""
name: Optional[str] = attribute()
ref: Optional[str] = attribute()
attributes: Array[Attribute] = array_element(name="attribute")
attribute_groups: Array["AttributeGroup"] = array_element(name="attributeGroup")
@property
def is_attribute(self) -> bool:
return True
@property
def real_type(self) -> Optional[str]:
return self.ref
[docs]@dataclass
class Any(AnnotationBase):
"""
<any
id = ID
maxOccurs = (nonNegativeInteger | unbounded) : 1
minOccurs = nonNegativeInteger : 1
namespace = ((##any | ##other) | List of (anyURI | (##targetNamespace | ##local)))
notNamespace = List of (anyURI | (##targetNamespace | ##local))
notQName = List of (QName | (##defined | ##definedSibling))
processContents = (lax | skip | strict) : strict
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</any>
"""
min_occurs: int = attribute(default=1)
max_occurs: int = attribute(default=1)
namespace: Optional[str] = attribute(default="##any")
process_contents: Optional[ProcessType] = attribute()
def __post_init__(self):
self.namespace = collapse_whitespace(self.namespace)
@property
def is_attribute(self) -> bool:
return True
@property
def is_wildcard(self) -> bool:
return True
@property
def real_name(self) -> str:
return f"{self.namespace}_element"
@property
def raw_namespace(self) -> Optional[str]:
return self.namespace
@property
def real_type(self) -> Optional[str]:
prefix = self.schema_prefix()
suffix = DataType.OBJECT.code
return f"{prefix}:{suffix}" if prefix else suffix
[docs] def get_restrictions(self) -> Dict[str, Anything]:
return {"min_occurs": self.min_occurs, "max_occurs": self.max_occurs}
[docs]@dataclass
class All(AnnotationBase):
"""
<all
id = ID
maxOccurs = (0 | 1) : 1
minOccurs = (0 | 1) : 1
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (element | any | group)*)
</all>
"""
min_occurs: int = attribute(default=1)
max_occurs: int = attribute(default=1)
any: Array[Any] = array_element(name="any")
elements: Array["Element"] = array_element(name="element")
groups: Array["Group"] = array_element(name="group")
[docs] def get_restrictions(self) -> Dict[str, Anything]:
return {"min_occurs": self.min_occurs, "max_occurs": self.max_occurs}
[docs]@dataclass
class Sequence(AnnotationBase):
"""
<sequence
id = ID
maxOccurs = (nonNegativeInteger | unbounded) : 1
minOccurs = nonNegativeInteger : 1
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (element | group | choice | sequence | any)*)
</sequence>
"""
min_occurs: int = attribute(default=1)
max_occurs: int = attribute(default=1)
elements: Array["Element"] = array_element(name="element")
groups: Array["Group"] = array_element(name="group")
choices: Array["Choice"] = array_element(name="choice")
sequences: Array["Sequence"] = array_element(name="sequence")
any: Array["Any"] = array_element()
[docs] def get_restrictions(self) -> Dict[str, Anything]:
return {
"min_occurs": self.min_occurs,
"max_occurs": self.max_occurs,
"sequential": True,
}
[docs]@dataclass
class Choice(AnnotationBase):
"""
<choice
id = ID
maxOccurs = (nonNegativeInteger | unbounded) : 1
minOccurs = nonNegativeInteger : 1
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (element | group | choice | sequence | any)*)
</choice>
"""
min_occurs: int = attribute(default=1)
max_occurs: int = attribute(default=1)
elements: Array["Element"] = array_element(name="element")
groups: Array["Group"] = array_element(name="group")
choices: Array["Choice"] = array_element(name="choice")
sequences: Array[Sequence] = array_element(name="sequence")
any: Array["Any"] = array_element()
[docs] def get_restrictions(self) -> Dict[str, Anything]:
return {
"min_occurs": self.min_occurs if self.min_occurs > 1 else 0,
"max_occurs": self.max_occurs,
}
[docs]@dataclass
class Group(AnnotationBase):
"""
<group
id = ID
maxOccurs = (nonNegativeInteger | unbounded) : 1
minOccurs = nonNegativeInteger : 1
name = NCName
ref = QName
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (all | choice | sequence)?)
</group>
"""
name: Optional[str] = attribute()
ref: Optional[str] = attribute()
min_occurs: int = attribute(default=1)
max_occurs: int = attribute(default=1)
all: Optional[All] = element()
choice: Optional[Choice] = element()
sequence: Optional[Sequence] = element()
@property
def is_attribute(self) -> bool:
return True
@property
def real_type(self) -> Optional[str]:
return self.ref
[docs] def get_restrictions(self) -> Dict[str, Anything]:
return {"min_occurs": self.min_occurs, "max_occurs": self.max_occurs}
[docs]@dataclass
class OpenContent(AnnotationBase):
"""
<openContent
id = ID
mode = (none | interleave | suffix) : interleave
{any attributes with non-schema namespace . . .}>
Content: (annotation?, any?)
</openContent>
"""
applies_to_empty: bool = attribute(default=False, name="appliesToEmpty")
mode: Mode = attribute(default=Mode.INTERLEAVE)
any: Any = element()
[docs]@dataclass
class DefaultOpenContent(OpenContent):
"""
<defaultOpenContent
appliesToEmpty = boolean : false
id = ID
mode = (interleave | suffix) : interleave
{any attributes with non-schema namespace . . .}>
Content: (annotation?, any)
</defaultOpenContent>
"""
[docs]@dataclass
class Extension(AnnotationBase):
"""
<extension
base = QName
id = ID
{any attributes with non-schema namespace . . .}>
Content: (annotation?, ((attribute | attributeGroup)*, anyAttribute?), assert*)
</extension>
"""
base: Optional[str] = attribute()
group: Optional[Group] = element()
all: Optional[All] = element()
choice: Optional[Choice] = element()
sequence: Optional[Sequence] = element()
any_attribute: Optional[AnyAttribute] = element()
open_content: Optional[OpenContent] = element(name="openContent")
attributes: Array[Attribute] = array_element(name="attribute")
attribute_groups: Array[AttributeGroup] = array_element(name="attributeGroup")
assertions: Array[Assertion] = array_element(name="assert")
@property
def extends(self) -> Optional[str]:
return self.base
[docs]@dataclass
class Enumeration(AnnotationBase):
"""
<enumeration
id = ID
value = anySimpleType
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</enumeration>
"""
value: str = attribute()
@property
def is_attribute(self) -> bool:
return True
@property
def real_type(self):
return None
@property
def real_name(self):
return self.value
@property
def default(self):
return self.value
[docs]@dataclass
class FractionDigits(AnnotationBase):
"""
<fractionDigits
fixed = boolean : false
id = ID
value = nonNegativeInteger
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</fractionDigits>
"""
value: int = attribute()
[docs]@dataclass
class Length(AnnotationBase):
"""
<length
fixed = boolean : false
id = ID
value = nonNegativeInteger
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</length>
"""
value: int = attribute()
[docs]@dataclass
class MaxExclusive(AnnotationBase):
"""
<maxExclusive
fixed = boolean : false
id = ID
value = anySimpleType
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</maxExclusive>
"""
value: float = attribute()
[docs]@dataclass
class MaxInclusive(AnnotationBase):
"""
<maxInclusive
fixed = boolean : false
id = ID
value = anySimpleType
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</maxInclusive>
"""
value: float = attribute()
[docs]@dataclass
class MaxLength(AnnotationBase):
"""
<maxLength
fixed = boolean : false
id = ID
value = nonNegativeInteger
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</maxLength>
"""
value: float = attribute()
[docs]@dataclass
class MinExclusive(AnnotationBase):
"""
<minExclusive
fixed = boolean : false
id = ID
value = anySimpleType
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</minExclusive>
"""
value: float = attribute()
[docs]@dataclass
class MinInclusive(AnnotationBase):
"""
<minInclusive
fixed = boolean : false
id = ID
value = anySimpleType
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</minInclusive>
"""
value: float = attribute()
[docs]@dataclass
class MinLength(AnnotationBase):
"""
<minLength
fixed = boolean : false
id = ID
value = nonNegativeInteger
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</minLength>
"""
value: float = attribute()
[docs]@dataclass
class Pattern(AnnotationBase):
"""
<pattern
id = ID
value = string
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</pattern>
"""
value: str = attribute()
[docs]@dataclass
class TotalDigits(AnnotationBase):
"""
<totalDigits
fixed = boolean : false
id = ID
value = positiveInteger
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</totalDigits>
"""
value: int = attribute()
[docs]@dataclass
class WhiteSpace(AnnotationBase):
"""
<whiteSpace
fixed = boolean : false
id = ID
value = (collapse | preserve | replace)
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</whiteSpace>
"""
value: str = attribute() # preserve, collapse, replace
[docs]@dataclass
class ExplicitTimezone(AnnotationBase):
"""
<explicitTimezone
fixed = boolean : false
id = ID
value = NCName
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</explicitTimezone>
"""
value: str = attribute()
fixed: bool = attribute(default=False)
[docs]@dataclass
class Restriction(AnnotationBase):
"""
<restriction
base = QName
id = ID
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (simpleType?, (
minExclusive | minInclusive | maxExclusive | maxInclusive |
totalDigits | fractionDigits | length | minLength | maxLength |
enumeration | whiteSpace | pattern | assertion | explicitTimezone |
{any with namespace: ##other})*)
)
</restriction>
"""
base: Optional[str] = attribute()
group: Optional[Group] = element()
all: Optional[All] = element()
choice: Optional[Choice] = element()
sequence: Optional[Sequence] = element()
open_content: Optional[OpenContent] = element(name="openContent")
attributes: Array[Attribute] = array_element(name="attribute")
attribute_groups: Array[AttributeGroup] = array_element(name="attributeGroup")
enumerations: Array[Enumeration] = array_element(name="enumeration")
asserts: Array[Assertion] = array_element(name="assert")
assertions: Array[Assertion] = array_element(name="assertion")
any_element: Array[object] = array_any_element()
min_exclusive: Optional[MinExclusive] = element()
min_inclusive: Optional[MinInclusive] = element()
min_length: Optional[MinLength] = element()
max_exclusive: Optional[MaxExclusive] = element()
max_inclusive: Optional[MaxInclusive] = element()
max_length: Optional[MaxLength] = element()
total_digits: Optional[TotalDigits] = element()
fraction_digits: Optional[FractionDigits] = element()
length: Optional[Length] = element()
white_space: Optional[WhiteSpace] = element()
patterns: Array[Pattern] = array_element(name="pattern")
explicit_timezone: Optional[ExplicitTimezone] = element()
simple_type: Optional[SimpleType] = element()
@property
def real_type(self) -> Optional[str]:
if self.simple_type:
return self.simple_type.real_type
return self.base
@property
def real_name(self) -> str:
return "value"
@property
def extends(self) -> Optional[str]:
return self.base
[docs] def get_restrictions(self) -> Dict[str, Anything]:
keys = (
"min_exclusive",
"min_inclusive",
"min_length",
"max_exclusive",
"max_inclusive",
"max_length",
"total_digits",
"fraction_digits",
"length",
"white_space",
"explicit_timezone",
)
restrictions = {
key: getattr(self, key).value
for key in keys
if getattr(self, key) is not None
}
if self.patterns:
restrictions["pattern"] = "|".join(
[pattern.value for pattern in self.patterns]
)
return restrictions
[docs]@dataclass
class SimpleContent(AnnotationBase):
"""
<simpleContent
id = ID
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (restriction | extension))
</simpleContent>
"""
restriction: Optional[Restriction] = element()
extension: Optional[Extension] = element()
[docs]@dataclass
class ComplexContent(SimpleContent):
"""
<complexContent
id = ID
mixed = boolean
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (restriction | extension))
</complexContent>
"""
mixed: bool = attribute(default=False)
[docs]@dataclass
class ComplexType(AnnotationBase):
"""
<complexType
abstract = boolean : false
block = (#all | List of (extension | restriction))
final = (#all | List of (extension | restriction))
id = ID
mixed = boolean
name = NCName
defaultAttributesApply = boolean : true
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (
simpleContent | complexContent |
(openContent?, (group | all | choice | sequence)?,
((attribute | attributeGroup)*, anyAttribute?), assert*))
)
</complexType>
"""
name: Optional[str] = attribute()
block: Optional[str] = attribute()
final: Optional[str] = attribute()
simple_content: Optional[SimpleContent] = element()
complex_content: Optional[ComplexContent] = element()
group: Optional[Group] = element()
all: Optional[All] = element()
choice: Optional[Choice] = element()
sequence: Optional[Sequence] = element()
any_attribute: Optional[AnyAttribute] = element()
open_content: Optional[OpenContent] = element(name="openContent")
attributes: Array[Attribute] = array_element(name="attribute")
attribute_groups: Array[AttributeGroup] = array_element(name="attributeGroup")
assertion: Array[Assertion] = array_element(name="assert")
abstract: bool = attribute(default=False)
mixed: bool = attribute(default=False)
default_attributes_apply: bool = attribute(
default=True, name="defaultAttributesApply"
)
@property
def is_mixed(self) -> bool:
if self.mixed:
return True
elif self.complex_content and self.complex_content.mixed:
return True
else:
return False
[docs]@dataclass
class Field(AnnotationBase):
"""
<field
id = ID
xpath = a subset of XPath expression, see below
xpathDefaultNamespace =
(anyURI | (##defaultNamespace | ##targetNamespace | ##local))
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</field>
"""
xpath: Optional[str] = attribute()
[docs]@dataclass
class Selector(Field):
"""
<selector
id = ID
xpath = a subset of XPath expression, see below
xpathDefaultNamespace =
(anyURI | (##defaultNamespace | ##targetNamespace | ##local))
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</selector>
"""
[docs]@dataclass
class Unique(AnnotationBase):
"""
<unique
id = ID
name = NCName
ref = QName
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (selector, field+)?)
</unique>
"""
name: Optional[str] = attribute()
selector: Optional[Selector] = element()
fields: Array[Field] = array_element(name="field")
[docs]@dataclass
class Key(AnnotationBase):
"""
<key
id = ID
name = NCName
ref = QName
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (selector, field+)?)
</key>
"""
name: Optional[str] = attribute()
selector: Optional[Selector] = element()
fields: Array[Selector] = array_element(name="field")
[docs]@dataclass
class Keyref(AnnotationBase):
"""
<keyref
id = ID
name = NCName
ref = QName
refer = QName
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (selector, field+)?)
</keyref>
"""
name: Optional[str] = attribute()
refer: Optional[str] = attribute()
selector: Optional[Selector] = element()
fields: Array[Selector] = array_element(name="field")
[docs]@dataclass
class Alternative(AnnotationBase):
"""
<alternative
id = ID
test = an XPath expression
type = QName
xpathDefaultNamespace =
(anyURI | (##defaultNamespace | ##targetNamespace | ##local))
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (simpleType | complexType)?)
</alternative>
"""
type: Optional[str] = attribute()
test: Optional[str] = attribute()
simple_type: Optional[SimpleType] = element()
complex_type: Optional[ComplexType] = element()
[docs]@dataclass
class Element(AnnotationBase):
"""
<element
abstract = boolean : false
block = (#all | List of (extension | restriction | substitution))
default = string
final = (#all | List of (extension | restriction))
fixed = string
form = (qualified | unqualified)
id = ID
maxOccurs = (nonNegativeInteger | unbounded) : 1
minOccurs = nonNegativeInteger : 1
name = NCName
nillable = boolean : false
ref = QName
substitutionGroup = List of QName
targetNamespace = anyURI
type = QName
{any attributes with non-schema namespace . . .}>
Content: (annotation?,
((simpleType | complexType)?, alternative*, (unique | key | keyref)*))
</element>
"""
name: Optional[str] = attribute()
id: Optional[str] = attribute()
ref: Optional[str] = attribute()
type: Optional[str] = attribute()
substitution_group: Optional[str] = attribute(name="substitutionGroup")
default: Optional[str] = attribute()
fixed: Optional[str] = attribute()
form: Optional[FormType] = attribute()
block: Optional[str] = attribute()
final: Optional[str] = attribute()
target_namespace: Optional[str] = attribute(name="targetNamespace")
simple_type: Optional[SimpleType] = element()
complex_type: Optional[ComplexType] = element()
alternatives: Array[Alternative] = array_element(name="alternative")
uniques: Array[Unique] = array_element(name="unique")
keys: Array[Key] = array_element(name="key")
keyrefs: Array[Keyref] = array_element(name="keyref")
min_occurs: int = attribute(default=1)
max_occurs: int = attribute(default=1)
nillable: bool = attribute(default=False)
abstract: bool = attribute(default=False)
@property
def is_attribute(self) -> bool:
return True
@property
def is_mixed(self) -> bool:
if self.complex_type:
return self.complex_type.is_mixed
else:
return False
@property
def raw_type(self) -> Optional[str]:
if self.type:
return self.type
elif self.has_children:
return None
else:
prefix = self.schema_prefix()
suffix = DataType.ANY_TYPE.code
return f"{prefix}:{suffix}" if prefix else suffix
@property
def real_type(self) -> Optional[str]:
types = set(
alternative.type for alternative in self.alternatives if alternative.type
)
if self.type:
types.add(self.type)
elif self.ref:
types.add(self.ref)
elif self.simple_type and self.simple_type.real_type:
types.add(self.simple_type.real_type)
return " ".join(sorted(types)) or None
@property
def substitutions(self) -> Array[str]:
if self.substitution_group:
return list(filter(None, self.substitution_group.split(" ")))
return list()
[docs] def get_restrictions(self) -> Dict[str, Anything]:
restrictions = {"min_occurs": self.min_occurs, "max_occurs": self.max_occurs}
if self.simple_type:
restrictions.update(self.simple_type.get_restrictions())
if self.nillable:
restrictions.update({"nillable": True})
return restrictions
[docs]@dataclass
class Notation(AnnotationBase):
"""
<notation
id = ID
name = NCName
public = token
system = anyURI
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</notation>
"""
name: Optional[str] = attribute()
public: Optional[str] = attribute()
system: Optional[str] = attribute()
[docs]@dataclass
class SchemaLocation(AnnotationBase):
location: Optional[Path] = field(default=None)
[docs]@dataclass
class Import(SchemaLocation):
"""
<import
id = ID
namespace = anyURI
schemaLocation = anyURI
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</import>
"""
namespace: Optional[str] = attribute()
schema_location: Optional[str] = attribute()
[docs]@dataclass
class Include(SchemaLocation):
"""
<include
id = ID
schemaLocation = anyURI
{any attributes with non-schema namespace . . .}>
Content: (annotation?)
</include>
"""
schema_location: Optional[str] = attribute()
[docs]@dataclass
class Redefine(SchemaLocation):
"""
<redefine
id = ID
schemaLocation = anyURI
{any attributes with non-schema namespace . . .}>
Content: (annotation | (simpleType | complexType | group | attributeGroup))*
</redefine>
"""
schema_location: Optional[str] = attribute()
simple_types: Array[SimpleType] = array_element(name="simpleType")
complex_types: Array[ComplexType] = array_element(name="complexType")
groups: Array[Group] = array_element(name="group")
attribute_groups: Array[AttributeGroup] = array_element(name="attributeGroup")
[docs]@dataclass
class Override(SchemaLocation):
"""
<override
id = ID
schemaLocation = anyURI
{any attributes with non-schema namespace . . .}>
Content: (
annotation | (simpleType | complexType | group |
attributeGroup | element | attribute | notation)
)*
</override>
"""
schema_location: Optional[str] = attribute()
simple_types: Array[SimpleType] = array_element(name="simpleType")
complex_types: Array[ComplexType] = array_element(name="complexType")
groups: Array[Group] = array_element(name="group")
attribute_groups: Array[AttributeGroup] = array_element(name="attributeGroup")
elements: Array[Element] = array_element(name="element")
attributes: Array[Attribute] = array_element(name="attribute")
notations: Array[Notation] = array_element(name="notation")
[docs]@dataclass
class Schema(SchemaLocation):
"""
<schema
attributeFormDefault = (qualified | unqualified) : unqualified
blockDefault = (#all | List of (extension | restriction | substitution)) : ''
defaultAttributes = QName
xpathDefaultNamespace =
(anyURI | (##defaultNamespace | ##targetNamespace | ##local)) : ##local
elementFormDefault = (qualified | unqualified) : unqualified
finalDefault = (#all | List of (extension | restriction | list | union)) : ''
id = ID
targetNamespace = anyURI
version = token
xml:lang = language
{any attributes with non-schema namespace . . .}>
Content: (
(include | import | redefine | override | annotation)*,
(defaultOpenContent, annotation*)?,
((simpleType | complexType | group | attributeGroup |
element | attribute | notation), annotation*)*)
</schema>
"""
target: Optional[str] = attribute()
block_default: Optional[str] = attribute()
default_attributes: Optional[str] = attribute(name="defaultAttributes")
final_default: Optional[str] = attribute()
target_namespace: Optional[str] = attribute()
version: Optional[str] = attribute()
xmlns: Optional[str] = attribute()
element_form_default: FormType = attribute(default=FormType.UNQUALIFIED)
attribute_form_default: FormType = attribute(default=FormType.UNQUALIFIED)
default_open_content: Optional[DefaultOpenContent] = element(
name="defaultOpenContent"
)
includes: Array[Include] = array_element(name="include")
imports: Array[Import] = array_element(name="import")
redefines: Array[Redefine] = array_element(name="redefine")
overrides: Array[Override] = array_element(name="override")
annotations: Array[Annotation] = array_element(name="annotation")
simple_types: Array[SimpleType] = array_element(name="simpleType")
complex_types: Array[ComplexType] = array_element(name="complexType")
groups: Array[Group] = array_element(name="group")
attribute_groups: Array[AttributeGroup] = array_element(name="attributeGroup")
elements: Array[Element] = array_element(name="element")
attributes: Array[Attribute] = array_element(name="attribute")
notations: Array[Notation] = array_element(name="notation")
[docs] def included(self) -> Iterator[UnionType[Import, Include, Redefine, Override]]:
for imp in self.imports:
yield imp
for inc in self.includes:
yield inc
for red in self.redefines:
yield red
for over in self.overrides:
yield over
@property
def module(self) -> str:
if self.location:
return self.location.name
if self.target_namespace:
return Path(self.target_namespace).stem
raise SchemaValueError("Unknown schema module")
@property
def target_prefix(self):
return next(
(
prefix
for prefix, namespace in self.nsmap.items()
if namespace == self.target_namespace
),
None,
)