.NET 6下如何让XML的condition属性引用当前Car节点的year属性
问题与解决方案
场景说明
使用.NET 6解析XML文件,由于.NET 6不支持XSD 1.1的xs:assert,通过XPath选中所有带condition属性的sg:Car节点,遍历并评估属性内的表达式是否为true。
XML文件内容:
<?xml version="1.0" encoding="utf-8"?> <sg:EmployeeDetails xmlns:sg="http://sg.iaea.org/ssac-qs/qsSchema.xsd"> <sg:FirstName>Sagar</sg:FirstName> <sg:MiddleName>H</sg:MiddleName> <sg:LastName>Shinde</sg:LastName> <sg:EmailId>sagarharidasshinde@gmail.com</sg:EmailId> <sg:Mobile>9021063389</sg:Mobile> <sg:Years> <sg:Year>1954</sg:Year> <sg:Year>1980</sg:Year> <sg:Year>1954</sg:Year> </sg:Years> <sg:Address>135/214, Hindustan Chowk, Mulund Colony, Mulund (W) - 400082</sg:Address> <sg:Cars> <sg:Car id="car1" year="1980" condition="count(./ancestor::sg:EmployeeDetails/sg:Years/sg:Year[text() = //sg:Car[@id='car1']/@year]) >=0" message="Driver must be 100 yrs old.">BMW</sg:Car> <sg:Car id="car2" year="1954" condition="count(./ancestor::sg:EmployeeDetails/sg:Years/sg:Year[text() = //sg:Car[@id='car1']/@year]) >=0" message="Driver must be 100 yrs old.">BMW</sg:Car> </sg:Cars> </sg:EmployeeDetails>
用于解析和验证的C#代码:
string xmlFilePath = "input.xml"; XmlReaderSettings settings = new XmlReaderSettings(); settings.Schemas.Add("http://sg.iaea.org/ssac-qs/qsSchema.xsd", "input.xsd"); settings.ValidationType = ValidationType.Schema; ValidationEventHandler eventHandler = new ValidationEventHandler(ValidationEventHandler); using (XmlReader reader = XmlReader.Create(xmlFilePath, settings)) { XmlDocument document = new XmlDocument(); document.Load(reader); var nsmgr = new XmlNamespaceManager(document.NameTable); nsmgr.AddNamespace("sg", "http://sg.iaea.org/ssac-qs/qsSchema.xsd"); document.Validate(eventHandler); // 读取XML内容并验证Schema var nav = document.CreateNavigator(); var list = nav.Select("//sg:Car[@condition]", nsmgr); while(list.MoveNext()) { var item = list.Current; var condition = item.GetAttribute("condition", ""); var result = item.Evaluate(condition, nsmgr); if (!(bool)result) { Console.WriteLine($"Validation error: {item.GetAttribute("message", "")}"); } } } // 验证事件处理函数 private static void ValidationEventHandler(object sender, ValidationEventArgs e) { switch (e.Severity) { case XmlSeverityType.Error: Console.WriteLine($"Schema Error: {e.Message}"); break; case XmlSeverityType.Warning: Console.WriteLine($"Schema Warning: {e.Message}"); break; } }
需求与问题
需要设置condition属性值,判断当前sg:Car节点的year属性值是否存在于其祖先sg:EmployeeDetails下的sg:Years/sg:Year元素中。当前表达式硬编码了特定Car节点的ID(如//sg:Car[@id='car1']),希望改为动态引用当前Car节点的year属性,但直接使用@year会在sg:Year节点上下文查找该属性,导致表达式执行失败。
解决方案
使用XPath的current()函数来指代当前正在评估的sg:Car节点。在谓词(方括号)内,上下文会切换到sg:Year节点,current()可以保留最初的Car节点上下文,从而正确获取其year属性。
正确的condition表达式如下(XML中需转义大于号>为>):
count(./ancestor::sg:EmployeeDetails/sg:Years/sg:Year[text() = current()/@year]) >= 0
修改后的XML中Car节点示例:
<sg:Car id="car1" year="1980" condition="count(./ancestor::sg:EmployeeDetails/sg:Years/sg:Year[text() = current()/@year]) >=0" message="Driver must be 100 yrs old.">BMW</sg:Car> <sg:Car id="car2" year="1954" condition="count(./ancestor::sg:EmployeeDetails/sg:Years/sg:Year[text() = current()/@year]) >=0" message="Driver must be 100 yrs old.">BMW</sg:Car>
原理说明
- 在XPath 1.0中,
current()函数返回的是当前正在处理的节点(即执行Evaluate方法时的sg:Car节点)。 - 当遍历到
sg:Year节点的谓词内时,上下文变为sg:Year,此时@year会指向sg:Year的属性(不存在),而current()/@year则会指向外层的sg:Car节点的year属性,从而实现正确的对比。
补充信息
对应的XSD文件:
<?xml version="1.0" encoding="utf-8"?> <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://sg.iaea.org/ssac-qs/qsSchema.xsd" xmlns:sg="http://sg.iaea.org/ssac-qs/qsSchema.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="EmployeeDetails"> <xs:complexType> <xs:sequence> <xs:element name="FirstName" type="xs:string"/> <xs:element name="MiddleName"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:minLength value="1"/> </xs:restriction> </xs:simpleType> </xs:element> <xs:element name="LastName" type="xs:string" /> <xs:element name="EmailId" type="xs:string" /> <xs:element name="Mobile" type="xs:integer" minOccurs="1" maxOccurs="1"/> <xs:element name="Years"> <xs:complexType> <xs:sequence> <xs:element name="Year" type="sg:Year" minOccurs="1" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="Address"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:minLength value="5"/> <xs:maxLength value="100"/> </xs:restriction> </xs:simpleType> </xs:element> <xs:element name="Cars" type="sg:Cars"/> </xs:sequence> </xs:complexType> </xs:element> <xs:complexType name="Cars"> <xs:sequence> <xs:element name="Car" type="sg:Car" maxOccurs="unbounded"> </xs:element> </xs:sequence> </xs:complexType> <xs:complexType name="Car"> <xs:simpleContent> <xs:extension base="sg:CarModel"> <xs:attribute name="id" type="xs:string" use="required"/> <xs:attribute name="year" type="sg:Year" use="required"/> <xs:attribute name="condition" type="xs:string" use="optional" /> <xs:attribute name="message" type="xs:string" use="optional" fixed="Driver must be 100 yrs old."/> </xs:extension> </xs:simpleContent> </xs:complexType> <xs:simpleType name="CarModel"> <xs:restriction base="xs:string"> <xs:enumeration value="Audi"/> <xs:enumeration value="Golf"/> <xs:enumeration value="BMW"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="Year"> <xs:restriction base="xs:int"> <xs:minInclusive value="1900" /> <xs:maxInclusive value="2200" /> </xs:restriction> </xs:simpleType> </xs:schema>
内容的提问来源于stack exchange,提问作者Raviraj Bhalerao
相关产品推荐
相关产品推荐

