Skip to content

Instantly share code, notes, and snippets.

@seesharprun
Last active December 3, 2025 16:24
Show Gist options
  • Select an option

  • Save seesharprun/c3aa21413e6254f62447abcd7030ea09 to your computer and use it in GitHub Desktop.

Select an option

Save seesharprun/c3aa21413e6254f62447abcd7030ea09 to your computer and use it in GitHub Desktop.
Parse XML

.NET script to parse XML and count elements

Uses XPath

Prerequisites

Usage

dotnet run parse.cs "<url>" "<xpath-query>"

dotnet run parse.cs "https://httpstatus.io/sitemap.xml" "//ns:url" 
#:package Spectre.Console@0.*
#:package Spectre.Console.Cli@0.*
#:property PublishAot=false
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Xml;
using System.Xml.XPath;
using Spectre.Console;
using Spectre.Console.Cli;
CommandApp<ParseXmlCommand> app = new();
return await app.RunAsync(args);
internal sealed class ParseXmlCommand : AsyncCommand<ParseXmlCommand.Settings>
{
public sealed class Settings : CommandSettings
{
[CommandArgument(0, "<URL>")]
[Description("The URL of the XML document to parse")]
public string Url { get; init; } = string.Empty;
[CommandArgument(1, "<XPATH>")]
[Description("The XPath query to execute against the XML document")]
public string XPath { get; init; } = string.Empty;
}
public override async Task<int> ExecuteAsync([NotNull] CommandContext context, [NotNull] Settings settings, CancellationToken cancellationToken)
{
try
{
await AnsiConsole.Status()
.Spinner(Spinner.Known.Dots)
.StartAsync("Fetching XML...", async ctx =>
{
using HttpClient httpClient = new();
string xmlContent = await httpClient.GetStringAsync(settings.Url, cancellationToken);
ctx.Status("Parsing XML...");
XmlDocument xmlDoc = new();
xmlDoc.LoadXml(xmlContent);
ctx.Status("Executing XPath query...");
XPathNavigator? navigator = xmlDoc.CreateNavigator();
if (navigator == null)
{
AnsiConsole.MarkupLine("[red]Failed to create XML navigator[/]");
return;
}
// Create namespace manager and auto-detect namespaces
XmlNamespaceManager namespaceManager = new(navigator.NameTable);
IDictionary<string, string> namespaces = navigator.GetNamespacesInScope(XmlNamespaceScope.All);
if (namespaces != null)
{
foreach (var ns in namespaces)
{
if (!string.IsNullOrEmpty(ns.Key))
{
namespaceManager.AddNamespace(ns.Key, ns.Value);
}
}
}
// If there's a default namespace, add it with a prefix
var defaultNamespace = xmlDoc.DocumentElement?.NamespaceURI;
if (!string.IsNullOrEmpty(defaultNamespace) && namespaces != null && !namespaces.Values.Contains(defaultNamespace))
{
namespaceManager.AddNamespace("ns", defaultNamespace);
AnsiConsole.MarkupLine($"[dim]Tip: Default namespace detected. Use 'ns:' prefix in XPath (e.g., //ns:url)[/]");
}
XPathNodeIterator? nodes = navigator.Select(settings.XPath, namespaceManager);
var table = new Table();
table.Border(TableBorder.Rounded);
table.AddColumn(new TableColumn("[bold yellow]Property[/]"));
table.AddColumn(new TableColumn("[bold cyan]Value[/]"));
table.AddRow("[grey]URL[/]", $"[link]{settings.Url}[/]");
table.AddRow("[grey]XPath[/]", $"[green]{settings.XPath}[/]");
table.AddRow("[grey]Nodes Found[/]", $"[bold blue]{nodes?.Count ?? 0}[/]");
AnsiConsole.WriteLine();
if (nodes != null && nodes.Count > 0)
{
AnsiConsole.WriteLine();
AnsiConsole.MarkupLine("[bold]Results:[/]");
var panel = new Panel(string.Join(Environment.NewLine,
nodes.Cast<XPathNavigator>().Select(n => n.Value)))
{
Border = BoxBorder.Rounded,
BorderStyle = new Style(Color.Green)
};
AnsiConsole.Write(panel);
}
AnsiConsole.WriteLine();
AnsiConsole.Write(table);
});
return 0;
}
catch (HttpRequestException ex)
{
AnsiConsole.MarkupLine($"[red bold]Error fetching URL:[/] {ex.Message}");
return 1;
}
catch (XmlException ex)
{
AnsiConsole.MarkupLine($"[red bold]Error parsing XML:[/] {ex.Message}");
return 1;
}
catch (XPathException ex)
{
AnsiConsole.MarkupLine($"[red bold]Error with XPath query:[/] {ex.Message}");
return 1;
}
catch (Exception ex)
{
AnsiConsole.MarkupLine($"[red bold]Error:[/] {ex.Message}");
return 1;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment