英文:
Select ancestor based on the attribute value of the selected node
问题
以下是翻译好的部分:
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);
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:Validation error");
}
}
}
如果您有其他问题或需要进一步的帮助,请随时提出。
英文:
input.xml file -
<?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]) &gt;=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]) &gt;=0" message="Driver must be 100 yrs old.">BMW</sg:Car>
</sg:Cars>
</sg:EmployeeDetails>
I am going to parse this xml file using .net 6. I will select all nodes with attribute 'condition' with xpath as -
//Car[@condition]
I will iterate thru' each node found with the captioned expression, and then evaluate if the expression in the value of the condition attribute is true. (I can not use xs:assert as .net6 does not support xsd1.1)
I would like to set the value of the condition attribute such that I can find if there is at least one ancestor node of 'EmployeeDetails' with more than one 'Year' child-node, having value that matches value of the 'year' attribute of the car node whose 'condition' attribute's value is being evaluated.
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);
// Read the XML content and validate against the 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:Validation error");
}
}
}
This code works fine.
I would to remove reference of '//sg:Car[@id='car1']' from
count(./ancestor::sg:EmployeeDetails/sg:Years/sg:Year[text() = //sg:Car[@id='car1']/@year]) &gt;=0
and make it something like
count(./ancestor::sg:EmployeeDetails/sg:Years/sg:Year[text() = @year]) &gt;=0
This exact expression fails as '@year' is searched in the sg:Year node instead of sg:Car node.
Does any one has any idea how to do that?
For completeness the input.xsd file is as below -
<?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>
答案1
得分: 0
关于XPath 1.0,我认为只需检查以下内容应该有效:
@year = ancestor::sg:EmployeeDetails/sg:Years/sg:Year
这是在sg:Car
元素的上下文中。只有当存在匹配的年份时,此条件才为真。
至于使用开源软件和XPath 3.1 来执行此操作,您可以使用 Saxon HE 10,通过 IKVM 转换为 .NET (Core),如下所示:
using Saxon.Api;
using System.Xml;
string xmlFilePath = "input2.xml";
XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas.Add("http://sg.iaea.org/ssac-qs/qsSchema.xsd", "input.xsd");
settings.ValidationType = ValidationType.Schema;
settings.ValidationEventHandler += (sender, e) =>
{
Console.WriteLine($"{e.Severity} {e.Message}");
};
using (XmlReader reader = XmlReader.Create(xmlFilePath, settings))
{
XmlDocument document = new XmlDocument();
document.Load(reader);
Processor processor = new Processor(false);
DocumentBuilder docBuilder = processor.NewDocumentBuilder();
XdmNode wrappedDoc = docBuilder.Wrap(document);
XPathCompiler xpathCompiler = processor.NewXPathCompiler();
xpathCompiler.DeclareNamespace("", "http://sg.iaea.org/ssac-qs/qsSchema.xsd");
xpathCompiler.DeclareNamespace("sg", "http://sg.iaea.org/ssac-qs/qsSchema.xsd");
foreach (XdmNode car in xpathCompiler.Evaluate("//Car[@condition]", wrappedDoc))
{
var conditionEvaluationResult = xpathCompiler.EvaluateSingle(car.GetAttributeValue("condition"), car) as XdmAtomicValue;
if (!conditionEvaluationResult.GetBooleanValue())
{
Console.WriteLine($"Validation failed for Car {car.GetAttributeValue("id")} with year {car.GetAttributeValue("year")}");
}
}
}
您的 .NET 项目应该包括以下内容:
<ItemGroup>
<PackageReference Include="SaxonHE10Net31Api" Version="10.9.0.1" />
</ItemGroup>
您的条件应该是(我认为您想要的是 >
而不是 >=
)例如:
<sg:Car id="car3" year="1984" condition="let $car := . return count(ancestor::sg:EmployeeDetails/sg:Years/sg:Year[. = $car/@year]) > 0" message="Driver must be 100 yrs old.">BMW</sg:Car>
示例 .NET 6 控制台项目位于 https://github.com/martin-honnen/SaxonHE10Net6XPath31Example1。
免责声明:Saxon 10 HE 是 Saxonica 公司的开源产品,仅适用于 .NET framework;上面使用和引用的 SaxonHE10Net31Api 项目是我为 .NET (Core) 即 .NET 3.1 及更高版本重新编译/重建的该开源项目。
英文:
In terms of XPath 1.0, I think it should work to just check
@year = ancestor::sg:EmployeeDetails/sg:Years/sg:Year
in the context of the sg:Car
element. That condition is only true if there is a year match.
As for doing it with open-source software and XPath 3.1, you can use Saxon HE 10 cross-compiled with IKVM to .NET (Core) as follows:
using Saxon.Api;
using System.Xml;
string xmlFilePath = "input2.xml";
XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas.Add("http://sg.iaea.org/ssac-qs/qsSchema.xsd", "input.xsd");
settings.ValidationType = ValidationType.Schema;
settings.ValidationEventHandler += (sender, e) =>
{
Console.WriteLine($"{e.Severity} {e.Message}");
};
using (XmlReader reader = XmlReader.Create(xmlFilePath, settings))
{
XmlDocument document = new XmlDocument();
document.Load(reader);
Processor processor = new Processor(false);
DocumentBuilder docBuilder = processor.NewDocumentBuilder();
XdmNode wrappedDoc = docBuilder.Wrap(document);
XPathCompiler xpathCompiler = processor.NewXPathCompiler();
xpathCompiler.DeclareNamespace("", "http://sg.iaea.org/ssac-qs/qsSchema.xsd");
xpathCompiler.DeclareNamespace("sg", "http://sg.iaea.org/ssac-qs/qsSchema.xsd");
foreach (XdmNode car in xpathCompiler.Evaluate("//Car[@condition]", wrappedDoc))
{
var conditionEvaluationResult = xpathCompiler.EvaluateSingle(car.GetAttributeValue("condition"), car) as XdmAtomicValue;
if (!conditionEvaluationResult.GetBooleanValue())
{
Console.WriteLine($"Validation failed for Car {car.GetAttributeValue("id")} with year {car.GetAttributeValue("year")}.");
}
}
}
Your .NET project would have e.g.
<ItemGroup>
<PackageReference Include="SaxonHE10Net31Api" Version="10.9.0.1" />
</ItemGroup>
your conditions would be (I think you want >
not >=
) e.g.
<sg:Car id="car3" year="1984" condition="let $car := . return count(ancestor::sg:EmployeeDetails/sg:Years/sg:Year[. = $car/@year]) &gt;0" message="Driver must be 100 yrs old.">BMW</sg:Car>
Example .NET 6 console project is at https://github.com/martin-honnen/SaxonHE10Net6XPath31Example1.
Disclaimer: Saxon 10 HE is an open-source product from Saxonica that exists on NuGet from Saxonica itself, but only for .NET framework; the project SaxonHE10Net31Api used and referenced above is my recompilation/rebuild of that open source project for .NET (Core) aka .NET 3.1 and later.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论