Skip to content

Commit 69842fc

Browse files
Selecting XML nodes with XPath: retarget net10.0, drop null-forgiving operators, add LINQ to XML XPath method and tests (#2199)
1 parent b44a542 commit 69842fc

6 files changed

Lines changed: 92 additions & 21 deletions

File tree

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
using SelectingXmlNodesWithXpath;
22
using System.Xml;
3+
using System.Xml.Linq;
4+
5+
var path = Path.Combine(AppContext.BaseDirectory, "BooksCatalog.xml");
36

47
var doc = new XmlDocument();
5-
doc.Load("BooksCatalog.xml");
8+
doc.Load(path);
69
var root = doc.DocumentElement!;
710

811
Console.WriteLine("Selected book:");
912
var singleResult = XmlNodesSelector.SelectSingleBook(root);
10-
Console.WriteLine(singleResult);
13+
Console.WriteLine(singleResult ?? "No book matched the query.");
1114

1215
Console.WriteLine("\nSelected books:");
1316
var results = XmlNodesSelector.SelectBooks(root);
1417
results.ForEach(Console.WriteLine);
1518

1619
Console.WriteLine("\nSelected books:");
1720
var resultsFromNamespaces = XmlNodesSelector.SelectBooksUsingNamespaces(doc);
18-
resultsFromNamespaces.ForEach(Console.WriteLine);
21+
resultsFromNamespaces.ForEach(Console.WriteLine);
22+
23+
Console.WriteLine("\nSelected books with LINQ to XML:");
24+
var linqResults = XmlNodesSelector.SelectBooksWithLinqToXml(XDocument.Load(path));
25+
linqResults.ForEach(Console.WriteLine);

‎xml-csharp/SelectingXmlNodesWithXpath/SelectingXmlNodesWithXpath/SelectingXmlNodesWithXpath.csproj‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net7.0</TargetFramework>
5+
<TargetFramework>net10.0</TargetFramework>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
88
</PropertyGroup>

‎xml-csharp/SelectingXmlNodesWithXpath/SelectingXmlNodesWithXpath/XmlNodesSelector.cs‎

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Xml;
22
using System.Xml.Linq;
3+
using System.Xml.XPath;
34

45
namespace SelectingXmlNodesWithXpath;
56

@@ -10,18 +11,23 @@ public static string FormatXml(string unformattedXml)
1011
return XElement.Parse(unformattedXml).ToString();
1112
}
1213

13-
public static string SelectSingleBook(XmlNode root)
14+
public static string? SelectSingleBook(XmlNode root)
1415
{
1516
var node = root.SelectSingleNode("//catalog/book[position()=2]");
16-
17-
return FormatXml(node!.OuterXml);
17+
18+
return node is null ? null : FormatXml(node.OuterXml);
1819
}
1920

2021
public static List<string> SelectBooks(XmlNode root)
2122
{
2223
var nodes = root.SelectNodes("//catalog/book[price<50.00]");
2324

24-
return nodes!
25+
if (nodes is null)
26+
{
27+
return [];
28+
}
29+
30+
return nodes
2531
.Cast<XmlNode>()
2632
.Select(x => FormatXml(x.OuterXml))
2733
.ToList();
@@ -34,9 +40,22 @@ public static List<string> SelectBooksUsingNamespaces(XmlDocument doc)
3440

3541
var nodes = doc.SelectNodes("descendant::ex:book", nsmgr);
3642

37-
return nodes!
43+
if (nodes is null)
44+
{
45+
return [];
46+
}
47+
48+
return nodes
3849
.Cast<XmlNode>()
3950
.Select(x => FormatXml(x.OuterXml))
4051
.ToList();
4152
}
53+
54+
public static List<string> SelectBooksWithLinqToXml(XDocument doc)
55+
{
56+
return doc
57+
.XPathSelectElements("//catalog/book[price<50.00]")
58+
.Select(x => x.ToString())
59+
.ToList();
60+
}
4261
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
global using SelectingXmlNodesWithXpath;
22
global using System.Xml;
3-
global using Xunit;
3+
global using Xunit;
4+
global using System.Xml.Linq;

‎xml-csharp/SelectingXmlNodesWithXpath/SelectingXmlNodesWithXpathTests/Tests.csproj‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net7.0</TargetFramework>
4+
<TargetFramework>net10.0</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77

@@ -16,13 +16,13 @@
1616
</ItemGroup>
1717

1818
<ItemGroup>
19-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
20-
<PackageReference Include="xunit" Version="2.4.2" />
21-
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
19+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.10.0" />
20+
<PackageReference Include="xunit" Version="2.9.3" />
21+
<PackageReference Include="xunit.runner.visualstudio" Version="4.0.0">
2222
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2323
<PrivateAssets>all</PrivateAssets>
2424
</PackageReference>
25-
<PackageReference Include="coverlet.collector" Version="3.2.0">
25+
<PackageReference Include="coverlet.collector" Version="10.0.1">
2626
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2727
<PrivateAssets>all</PrivateAssets>
2828
</PackageReference>

‎xml-csharp/SelectingXmlNodesWithXpath/SelectingXmlNodesWithXpathTests/XmlNodesSelectorTest.cs‎

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,18 +64,20 @@ public void GivenAnXmlFile_WhenSelectingASingleNode_ThenReturnsTheSecondPosition
6464
{
6565
var result = XmlNodesSelector.SelectSingleBook(_document.DocumentElement!);
6666

67-
Assert.Equal(result, _expectedResults["Book2"]);
67+
Assert.Equal(result?.ReplaceLineEndings(), _expectedResults["Book2"].ReplaceLineEndings());
6868
}
6969

7070
[Fact]
7171
public void GivenAnXmlFile_WhenSelectingNodes_ThenReturnBooksWithPriceLowerThan50()
7272
{
7373
var expected = _expectedResults
7474
.Where(pair => pair.Key is "Book1" or "Book3")
75-
.Select(pair => pair.Value)
75+
.Select(pair => pair.Value.ReplaceLineEndings())
7676
.ToList();
7777

78-
var result = XmlNodesSelector.SelectBooks(_document.DocumentElement!);
78+
var result = XmlNodesSelector.SelectBooks(_document.DocumentElement!)
79+
.Select(x => x.ReplaceLineEndings())
80+
.ToList();
7981

8082
Assert.Equal(result, expected);
8183
}
@@ -86,11 +88,53 @@ public void GivenAnXmlFile_WhenSelectingNodesUsingNamespaces_ThenReturnBookEleme
8688
var expected =
8789
_expectedResults
8890
.Where(pair => pair.Key is "Book4")
89-
.Select(pair => pair.Value)
91+
.Select(pair => pair.Value.ReplaceLineEndings())
9092
.ToList();
9193

92-
var result = XmlNodesSelector.SelectBooksUsingNamespaces(_document);
94+
var result = XmlNodesSelector.SelectBooksUsingNamespaces(_document)
95+
.Select(x => x.ReplaceLineEndings())
96+
.ToList();
9397

9498
Assert.Equal(result, expected);
9599
}
96-
}
100+
101+
[Fact]
102+
public void GivenAnXmlFile_WhenSelectSingleNodeMatchesNothing_ThenReturnsNull()
103+
{
104+
var node = _document.DocumentElement!.SelectSingleNode("//catalog/book[price>1000]");
105+
106+
Assert.Null(node);
107+
}
108+
109+
[Fact]
110+
public void GivenAnXmlFile_WhenSelectNodesMatchesNothing_ThenReturnsAnEmptyList()
111+
{
112+
var nodes = _document.DocumentElement!.SelectNodes("//catalog/book[price>1000]");
113+
114+
Assert.NotNull(nodes);
115+
Assert.Equal(0, nodes.Count);
116+
}
117+
118+
[Fact]
119+
public void GivenABookInADefaultNamespace_WhenQueryingWithAnUnprefixedName_ThenReturnsThreeOfTheFourBooks()
120+
{
121+
var nodes = _document.DocumentElement!.SelectNodes("//catalog/book");
122+
123+
Assert.NotNull(nodes);
124+
Assert.Equal(3, nodes.Count);
125+
}
126+
127+
[Fact]
128+
public void GivenAnXDocument_WhenSelectingWithXPathSelectElements_ThenReturnsTheSameBooksAsSelectNodes()
129+
{
130+
var expected = XmlNodesSelector.SelectBooks(_document.DocumentElement!)
131+
.Select(x => x.ReplaceLineEndings())
132+
.ToList();
133+
134+
var result = XmlNodesSelector.SelectBooksWithLinqToXml(XDocument.Load("BooksCatalog.xml"))
135+
.Select(x => x.ReplaceLineEndings())
136+
.ToList();
137+
138+
Assert.Equal(expected, result);
139+
}
140+
}

0 commit comments

Comments
 (0)