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

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

Select ancestor based on the attribute value of the selected node

问题

以下是翻译好的部分:

  1. string xmlFilePath = "input.xml";
  2. XmlReaderSettings settings = new XmlReaderSettings();
  3. settings.Schemas.Add("http://sg.iaea.org/ssac-qs/qsSchema.xsd", "input.xsd");
  4. settings.ValidationType = ValidationType.Schema;
  5. ValidationEventHandler eventHandler = new ValidationEventHandler(ValidationEventHandler);
  6. using (XmlReader reader = XmlReader.Create(xmlFilePath, settings))
  7. {
  8. XmlDocument document = new XmlDocument();
  9. document.Load(reader);
  10. var nsmgr = new XmlNamespaceManager(document.NameTable);
  11. nsmgr.AddNamespace("sg", "http://sg.iaea.org/ssac-qs/qsSchema.xsd");
  12. document.Validate(eventHandler);
  13. var nav = document.CreateNavigator();
  14. var list = nav.Select("//sg:Car[@condition]", nsmgr);
  15. while (list.MoveNext())
  16. {
  17. var item = list.Current;
  18. var condition = item.GetAttribute("condition", "");
  19. var result = item.Evaluate(condition, nsmgr);
  20. if (!(bool)result)
  21. {
  22. Console.WriteLine("Validation error:Validation error");
  23. }
  24. }
  25. }

如果您有其他问题或需要进一步的帮助,请随时提出。

英文:

input.xml file -

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <sg:EmployeeDetails xmlns:sg="http://sg.iaea.org/ssac-qs/qsSchema.xsd">
  3. <sg:FirstName>Sagar</sg:FirstName>
  4. <sg:MiddleName>H</sg:MiddleName>
  5. <sg:LastName>Shinde</sg:LastName>
  6. <sg:EmailId>sagarharidasshinde@gmail.com</sg:EmailId>
  7. <sg:Mobile>9021063389</sg:Mobile>
  8. <sg:Years>
  9. <sg:Year>1954</sg:Year>
  10. <sg:Year>1980</sg:Year>
  11. <sg:Year>1954</sg:Year>
  12. </sg:Years>
  13. <sg:Address>135/214, Hindustan Chowk, Mulund Colony, Mulund (W) - 400082</sg:Address>
  14. <sg:Cars>
  15. <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>
  16. <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>
  17. </sg:Cars>
  18. </sg:EmployeeDetails>

I am going to parse this xml file using .net 6. I will select all nodes with attribute 'condition' with xpath as -

  1. //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.

  1. string xmlFilePath = "input.xml";
  2. XmlReaderSettings settings = new XmlReaderSettings();
  3. settings.Schemas.Add("http://sg.iaea.org/ssac-qs/qsSchema.xsd", "input.xsd");
  4. settings.ValidationType = ValidationType.Schema;
  5. ValidationEventHandler eventHandler = new ValidationEventHandler(ValidationEventHandler);
  6. using (XmlReader reader = XmlReader.Create(xmlFilePath, settings))
  7. {
  8. XmlDocument document = new XmlDocument();
  9. document.Load(reader);
  10. var nsmgr = new XmlNamespaceManager(document.NameTable);
  11. nsmgr.AddNamespace("sg", "http://sg.iaea.org/ssac-qs/qsSchema.xsd");
  12. document.Validate(eventHandler);
  13. // Read the XML content and validate against the schema
  14. var nav = document.CreateNavigator();
  15. var list = nav.Select("//sg:Car[@condition]", nsmgr);
  16. while(list.MoveNext())
  17. {
  18. var item = list.Current;
  19. var condition = item.GetAttribute("condition", "");
  20. var result = item.Evaluate(condition, nsmgr);
  21. if (!(bool)result)
  22. {
  23. Console.WriteLine($"Validation error:Validation error");
  24. }
  25. }
  26. }

This code works fine.

I would to remove reference of '//sg:Car[@id='car1']' from

  1. count(./ancestor::sg:EmployeeDetails/sg:Years/sg:Year[text() = //sg:Car[@id='car1']/@year]) >=0

and make it something like

  1. 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 -

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
  3. targetNamespace="http://sg.iaea.org/ssac-qs/qsSchema.xsd"
  4. xmlns:sg="http://sg.iaea.org/ssac-qs/qsSchema.xsd"
  5. xmlns:xs="http://www.w3.org/2001/XMLSchema">
  6. <xs:element name="EmployeeDetails">
  7. <xs:complexType>
  8. <xs:sequence>
  9. <xs:element name="FirstName" type="xs:string"/>
  10. <xs:element name="MiddleName">
  11. <xs:simpleType>
  12. <xs:restriction base="xs:string">
  13. <xs:minLength value="1"/>
  14. </xs:restriction>
  15. </xs:simpleType>
  16. </xs:element>
  17. <xs:element name="LastName" type="xs:string" />
  18. <xs:element name="EmailId" type="xs:string" />
  19. <xs:element name="Mobile" type="xs:integer" minOccurs="1" maxOccurs="1"/>
  20. <xs:element name="Years">
  21. <xs:complexType>
  22. <xs:sequence>
  23. <xs:element name="Year" type="sg:Year" minOccurs="1" maxOccurs="unbounded"/>
  24. </xs:sequence>
  25. </xs:complexType>
  26. </xs:element>
  27. <xs:element name="Address">
  28. <xs:simpleType>
  29. <xs:restriction base="xs:string">
  30. <xs:minLength value="5"/>
  31. <xs:maxLength value="100"/>
  32. </xs:restriction>
  33. </xs:simpleType>
  34. </xs:element>
  35. <xs:element name="Cars" type="sg:Cars"/>
  36. </xs:sequence>
  37. </xs:complexType>
  38. </xs:element>
  39. <xs:complexType name="Cars">
  40. <xs:sequence>
  41. <xs:element name="Car" type="sg:Car" maxOccurs="unbounded">
  42. </xs:element>
  43. </xs:sequence>
  44. </xs:complexType>
  45. <xs:complexType name="Car">
  46. <xs:simpleContent>
  47. <xs:extension base="sg:CarModel">
  48. <xs:attribute name="id" type="xs:string" use="required"/>
  49. <xs:attribute name="year" type="sg:Year" use="required"/>
  50. <xs:attribute name="condition" type="xs:string" use="optional" />
  51. <xs:attribute name="message" type="xs:string" use="optional" fixed="Driver must be 100 yrs old."/>
  52. </xs:extension>
  53. </xs:simpleContent>
  54. </xs:complexType>
  55. <xs:simpleType name="CarModel">
  56. <xs:restriction base="xs:string">
  57. <xs:enumeration value="Audi"/>
  58. <xs:enumeration value="Golf"/>
  59. <xs:enumeration value="BMW"/>
  60. </xs:restriction>
  61. </xs:simpleType>
  62. <xs:simpleType name="Year">
  63. <xs:restriction base="xs:int">
  64. <xs:minInclusive value="1900" />
  65. <xs:maxInclusive value="2200" />
  66. </xs:restriction>
  67. </xs:simpleType>
  68. </xs:schema>

答案1

得分: 0

关于XPath 1.0,我认为只需检查以下内容应该有效:

  1. @year = ancestor::sg:EmployeeDetails/sg:Years/sg:Year

这是在sg:Car元素的上下文中。只有当存在匹配的年份时,此条件才为真。

至于使用开源软件和XPath 3.1 来执行此操作,您可以使用 Saxon HE 10,通过 IKVM 转换为 .NET (Core),如下所示:

  1. using Saxon.Api;
  2. using System.Xml;
  3. string xmlFilePath = "input2.xml";
  4. XmlReaderSettings settings = new XmlReaderSettings();
  5. settings.Schemas.Add("http://sg.iaea.org/ssac-qs/qsSchema.xsd", "input.xsd");
  6. settings.ValidationType = ValidationType.Schema;
  7. settings.ValidationEventHandler += (sender, e) =>
  8. {
  9. Console.WriteLine($"{e.Severity} {e.Message}");
  10. };
  11. using (XmlReader reader = XmlReader.Create(xmlFilePath, settings))
  12. {
  13. XmlDocument document = new XmlDocument();
  14. document.Load(reader);
  15. Processor processor = new Processor(false);
  16. DocumentBuilder docBuilder = processor.NewDocumentBuilder();
  17. XdmNode wrappedDoc = docBuilder.Wrap(document);
  18. XPathCompiler xpathCompiler = processor.NewXPathCompiler();
  19. xpathCompiler.DeclareNamespace("", "http://sg.iaea.org/ssac-qs/qsSchema.xsd");
  20. xpathCompiler.DeclareNamespace("sg", "http://sg.iaea.org/ssac-qs/qsSchema.xsd");
  21. foreach (XdmNode car in xpathCompiler.Evaluate("//Car[@condition]", wrappedDoc))
  22. {
  23. var conditionEvaluationResult = xpathCompiler.EvaluateSingle(car.GetAttributeValue("condition"), car) as XdmAtomicValue;
  24. if (!conditionEvaluationResult.GetBooleanValue())
  25. {
  26. Console.WriteLine($"Validation failed for Car {car.GetAttributeValue("id")} with year {car.GetAttributeValue("year")}");
  27. }
  28. }
  29. }

您的 .NET 项目应该包括以下内容:

  1. <ItemGroup>
  2. <PackageReference Include="SaxonHE10Net31Api" Version="10.9.0.1" />
  3. </ItemGroup>

您的条件应该是(我认为您想要的是 > 而不是 >=)例如:

  1. <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

  1. @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:

  1. using Saxon.Api;
  2. using System.Xml;
  3. string xmlFilePath = &quot;input2.xml&quot;;
  4. XmlReaderSettings settings = new XmlReaderSettings();
  5. settings.Schemas.Add(&quot;http://sg.iaea.org/ssac-qs/qsSchema.xsd&quot;, &quot;input.xsd&quot;);
  6. settings.ValidationType = ValidationType.Schema;
  7. settings.ValidationEventHandler += (sender, e) =&gt;
  8. {
  9. Console.WriteLine($&quot;{e.Severity} {e.Message}&quot;);
  10. };
  11. using (XmlReader reader = XmlReader.Create(xmlFilePath, settings))
  12. {
  13. XmlDocument document = new XmlDocument();
  14. document.Load(reader);
  15. Processor processor = new Processor(false);
  16. DocumentBuilder docBuilder = processor.NewDocumentBuilder();
  17. XdmNode wrappedDoc = docBuilder.Wrap(document);
  18. XPathCompiler xpathCompiler = processor.NewXPathCompiler();
  19. xpathCompiler.DeclareNamespace(&quot;&quot;, &quot;http://sg.iaea.org/ssac-qs/qsSchema.xsd&quot;);
  20. xpathCompiler.DeclareNamespace(&quot;sg&quot;, &quot;http://sg.iaea.org/ssac-qs/qsSchema.xsd&quot;);
  21. foreach (XdmNode car in xpathCompiler.Evaluate(&quot;//Car[@condition]&quot;, wrappedDoc))
  22. {
  23. var conditionEvaluationResult = xpathCompiler.EvaluateSingle(car.GetAttributeValue(&quot;condition&quot;), car) as XdmAtomicValue;
  24. if (!conditionEvaluationResult.GetBooleanValue())
  25. {
  26. Console.WriteLine($&quot;Validation failed for Car {car.GetAttributeValue(&quot;id&quot;)} with year {car.GetAttributeValue(&quot;year&quot;)}.&quot;);
  27. }
  28. }
  29. }

Your .NET project would have e.g.

  1. &lt;ItemGroup&gt;
  2. &lt;PackageReference Include=&quot;SaxonHE10Net31Api&quot; Version=&quot;10.9.0.1&quot; /&gt;
  3. &lt;/ItemGroup&gt;

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

  1. &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:

确定