根据所选节点的属性值选择祖先节点

huangapple go评论85阅读模式
英文:

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]) >=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>

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]) >=0

and make it something like

count(./ancestor::sg:EmployeeDetails/sg:Years/sg:Year[text() = @year]) >=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]) &gt; 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 = &quot;input2.xml&quot;;
XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas.Add(&quot;http://sg.iaea.org/ssac-qs/qsSchema.xsd&quot;, &quot;input.xsd&quot;);
settings.ValidationType = ValidationType.Schema;
settings.ValidationEventHandler += (sender, e) =&gt;
{
    Console.WriteLine($&quot;{e.Severity} {e.Message}&quot;);
};

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(&quot;&quot;, &quot;http://sg.iaea.org/ssac-qs/qsSchema.xsd&quot;);
    xpathCompiler.DeclareNamespace(&quot;sg&quot;, &quot;http://sg.iaea.org/ssac-qs/qsSchema.xsd&quot;);

    foreach (XdmNode car in xpathCompiler.Evaluate(&quot;//Car[@condition]&quot;, wrappedDoc))
    {
        var conditionEvaluationResult = xpathCompiler.EvaluateSingle(car.GetAttributeValue(&quot;condition&quot;), car) as XdmAtomicValue;
        if (!conditionEvaluationResult.GetBooleanValue())
        {
            Console.WriteLine($&quot;Validation failed for Car {car.GetAttributeValue(&quot;id&quot;)} with year {car.GetAttributeValue(&quot;year&quot;)}.&quot;);
        }
    }

}

Your .NET project would have e.g.

  &lt;ItemGroup&gt;
    &lt;PackageReference Include=&quot;SaxonHE10Net31Api&quot; Version=&quot;10.9.0.1&quot; /&gt;
  &lt;/ItemGroup&gt;

your conditions would be (I think you want &gt; not &gt;=) e.g.

&lt;sg:Car id=&quot;car3&quot; year=&quot;1984&quot; condition=&quot;let $car := . return count(ancestor::sg:EmployeeDetails/sg:Years/sg:Year[. = $car/@year]) &amp;gt;0&quot; message=&quot;Driver must be 100 yrs old.&quot;&gt;BMW&lt;/sg:Car&gt;

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.

huangapple
  • 本文由 发表于 2023年7月31日 20:13:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/76803509.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定