Dev:Using xsi:type

From railML 3 Wiki
Jump to navigation Jump to search

Using xsi:type
 

When using xsi:type and what to respect before starting?

  • Before implementing any extensions to the railML® schema, please consider Dev:Extending_railML#when.
  • The means described in this page using the xsi:type-attribute) is proposed from railML® 3.2 onwards. For railML® 2 and railML® 3.1, the proposed means are xs:any, xs:anyAttribute and rail:tOtherEnumerationValue, comp. Dev:UsingAny.
  • Please, take care of appropriate namespace handling.

Motivation

Extension of railML in earlier versions

Extensions to railML have been around for a long time. Many suppliers find that some details of their particular use case may not have found their way into the standard. However, since partners may already support railML, the general structure of the data is already clear and can be exchanged while only a few details need adding. This common scenario often led to the implementation of extensions that allow adding these few extra points of data with minimal effort for both parties of the data exchange.

With railML 2.x as well as railML 3.1 these extensions were usually build on the basis of the <xs:any> any schema element that allows specification of any subelement for a railML element as long as it is defined in another namespace.

<xs:complexType name="eTrack">
	<xs:complexContent>
		<xs:extension base="rail:tTrack">
			<xs:sequence>
				<xs:element name="trackDescr" type="xs:string" ... />
				<xs:any namespace="##other" 
					processContents="strict"
					minOccurs="0"
					maxOccurs="unbounded">
				</xs:any>
			</xs:sequence>
		</xs:extension>
	</xs:complexContent>
</xs:complexType>

The new elements could be specified in a separate XSD but had one major drawback. It was not possible to specify for with element an extension element was meant. This means that if an extension element “fireStation” that described a firestation located at a certain point in the infrastructure could be used as presumably intended with OCPs, but it could also be used in a rather unpredictable way, like for example for derailers or signals. With the <xs:any> technique it is basically impossible to restrict usage of an extended element to a certain element. The reason for this is that when implementing the extension schema the validator cannot know if the extended element would be there due to the <xs:any> or due to the specified extra element. Thus, the extension XSD would not be valid and could not be used.

Goals of removal of anyElement

With removal of the <xs:any> this has changed. It now is possible to restrict the usage of newly introduced elements to particular railML base elements. It however requires a slightly different approach when defining these extensions. How this is to be done is described in the following chapter.

The benefit of this change is that now it is possible to validate an extended railML, including the validation of the extension elements. Additionally, as the new way of describing this more precise it allows for code generation. Before code generation was possible for standard railML, but the extensions could not be considered due to the indeterminacy of the placement of the extended elements.

How to use xsi:type

Example

Schema extension

In the following example extension two separate ways of extending railML 3.2 are shown. They are different in that they follow different approaches on how to model a certain information. The general approach is the same for both.

<?xml version="1.0" encoding="UTF-8"?>
<railML 
	xmlns="https://www.railml.org/schemas/3.2" 
	xmlns:example="https://www.somedomain.net/2022/exampleExtension" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="https://www.somedomain.net/2022/exampleExtension ExampleExtension.xsd https://www.railml.org/schemas/3.2 https://schemas.railml.org/3.2/railml3.xsd" 
	version="0.1">
	
	<infrastructure id="is01">
		<functionalInfrastructure>
			<operationalPoints>
				<operationalPoint xsi:type="example:ExtendedOperationalPoint" id="op01">
					<!-- You can use all elements and attributes of the original operational point in addition to the ones we added -->
					<name name="Praha" language="cz"/>
					<name name="Prag" language="de"/>
					<name name="Prague" language="en"/>
					<example:priceCategory example:categoryNumber="2" example:priceMultiplier="2.1">
						<example:name name="Tourist fare" language="en"/>
					</example:priceCategory>
				</operationalPoint>
			</operationalPoints>

			<!-- The same applies to the extended tracks -->
			<tracks>
				<track xsi:type="example:ExtendedTrack" id="tr01" type="mainTrack"
					example:additionalExampleComment="This attribute was added just to show how to extend a type with an additional attribute.">
					<!-- You can use all elements and attributes of the original track in addition to the ones we added -->
					<designator register="_Tracks" entry="1234"/>
					<length value="500" type="physical"/>
					<example:priceCategory example:categoryNumber="1" example:priceMultiplier="1.0">
						<example:name name="Normal fare" language="en"/>
					</example:priceCategory>
					<example:priceCategory example:categoryNumber="3" example:priceMultiplier="1.5">
						<example:name name="Express fare" language="en"/>
					</example:priceCategory>
				</track>
				<track id="tr02" type="sidingTrack">
					<!-- You can also use the original type-->
					<designator register="_Tracks" entry="4321"/>
					<length value="1200" type="physical"/>
				</track>
			</tracks>
			
		</functionalInfrastructure>
	</infrastructure>
</railML>

The idea was to model price categories that could be applied to tracks as well as to operational points. The contents are completely fictional and are not to be used in any operational context.

The first approach basically simply extends the original railml elements track and operationalPoint of railML 3.2 and adds a new sub element “priceCategory” that is required and can be repeated. The element consists of two attributes, one required and one optional as well as a repeatable subelement “name”.

The other approach is the idea of creating a price category as a new functional infrastructure element that can be located on the topology the same way all other functional infrastructure elements are. Regarding its content the second approach is no different than the first one.

These are just ideas on how to model a price category. They should demonstrate how to extend railML 3.2 and help understanding the basic principles.

Example of the first approach

<?xml version="1.0" encoding="UTF-8"?>
<railML 
	xmlns="https://www.railml.org/schemas/3.2" 
	xmlns:example="https://www.somedomain.net/2022/exampleExtension" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="https://www.somedomain.net/2022/exampleExtension ExampleExtension.xsd https://www.railml.org/schemas/3.2 https://schemas.railml.org/3.2/railml3.xsd" 
	version="0.1">
	
	<infrastructure id="is01">
		<functionalInfrastructure>
			<operationalPoints>
				<operationalPoint xsi:type="example:ExtendedOperationalPoint" id="op01">
					<!-- You can use all elements and attributes of the original operational point in addition to the ones we added -->
					<name name="Praha" language="cz"/>
					<name name="Prag" language="de"/>
					<name name="Prague" language="en"/>
					<example:priceCategory example:categoryNumber="2" example:priceMultiplier="2.1">
						<example:name name="Tourist fare" language="en"/>
					</example:priceCategory>
				</operationalPoint>
			</operationalPoints>

			<!-- The same applies to the extended tracks -->
			<tracks>
				<track xsi:type="example:ExtendedTrack" id="tr01" type="mainTrack"
					example:additionalExampleComment="This attribute was added just to show how to extend a type with an additional attribute.">
					<!-- You can use all elements and attributes of the original track in addition to the ones we added -->
					<designator register="_Tracks" entry="1234"/>
					<length value="500" type="physical"/>
					<example:priceCategory example:categoryNumber="1" example:priceMultiplier="1.0">
						<example:name name="Normal fare" language="en"/>
					</example:priceCategory>
					<example:priceCategory example:categoryNumber="3" example:priceMultiplier="1.5">
						<example:name name="Express fare" language="en"/>
					</example:priceCategory>
				</track>
				<track id="tr02" type="sidingTrack">
					<!-- You can also use the original type-->
					<designator register="_Tracks" entry="4321"/>
					<length value="1200" type="physical"/>
				</track>
			</tracks>
			
		</functionalInfrastructure>
	</infrastructure>
</railML>

Example of the second approach

<?xml version="1.0" encoding="UTF-8"?>
<railML 
	xmlns="https://www.railml.org/schemas/3.2" 
	xmlns:example="https://www.somedomain.net/2022/exampleExtension" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="https://www.somedomain.net/2022/exampleExtension ExampleExtension.xsd https://www.railml.org/schemas/3.2 https://schemas.railml.org/3.2/railml3.xsd" 
	version="0.1">
	<infrastructure id="is01">
		<topology>
			<netElements>
				<netElement id="ne01">
					<associatedPositioningSystem id="aps01">
						<intrinsicCoordinate id="ic01" intrinsicCoord="0"></intrinsicCoordinate>
					</associatedPositioningSystem>
				</netElement>
			</netElements>
			<networks>
				<network id="nw01">
					<level id="lv01"></level>
				</network>
			</networks>
		</topology>
		<functionalInfrastructure xsi:type="example:ExtendedFunctionalInfrastructure">
			<operationalPoints>
				<operationalPoint id="op01"></operationalPoint>
				<operationalPoint id="op02"></operationalPoint>
			</operationalPoints>			<tracks>
				<track id="tr01" type="mainTrack"></track>
				<track id="tr02" type="mainTrack"></track>
			</tracks>
			<example:priceCategories>
				<example:priceCategory id="pc01" example:categoryNumber="4" example:priceMultiplier="1.2">
					<linearLocation id="ll01">
						<associatedNetElement netElementRef="ne01" keepsOrientation="true"></associatedNetElement>
					</linearLocation>
					<example:name name="Example category" language="en"></example:name>
				</example:priceCategory>
			</example:priceCategories>
		</functionalInfrastructure>
	</infrastructure>
</railML>

Disclaimer

It needs to be understood that extensions should only be used if the underlying requirements cannot be fulfilled with standard railML. If unsure it is a good idea to post a question in the forum or contact a railML coordinator directly. In general, it is a good idea to communicate extensions with railML.org as it allows us to understand the needs of the railML community and include new aspects needed by the users in a new version of railML.