<PackageReference Include="NUnit" Version="4.2.2" />

TNode

public sealed class TNode
TNode represents a single node in the XML representation of a Test or TestResult. It replaces System.Xml.XmlNode and System.Xml.Linq.XElement, providing a minimal set of methods for operating on the XML in a platform-independent manner.
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Xml; namespace NUnit.Framework.Interfaces { [System.Runtime.CompilerServices.NullableContext(1)] [System.Runtime.CompilerServices.Nullable(0)] [DebuggerDisplay("{OuterXml}")] public sealed class TNode { [System.Runtime.CompilerServices.Nullable(0)] private sealed class NodeFilter { private readonly string _nodeName; [System.Runtime.CompilerServices.Nullable(2)] private readonly string _propName; [System.Runtime.CompilerServices.Nullable(2)] private readonly string _propValue; private static readonly char[] TrimChars = new char[3] { ' ', '"', '\'' }; public NodeFilter(string xpath) { _nodeName = xpath; int num = xpath.IndexOf('['); if (num >= 0) { if (!xpath.EndsWith("]", StringComparison.Ordinal)) throw new ArgumentException("Invalid property expression", "xpath"); _nodeName = xpath.Substring(0, num); string text = xpath.Substring(num + 1, xpath.Length - num - 2); int num2 = text.IndexOf('='); if (num2 < 0 || text[0] != '@') throw new ArgumentException("Invalid property expression", "xpath"); _propName = text.Substring(1, num2 - 1).Trim(); _propValue = text.Substring(num2 + 1).Trim(TrimChars); } } public bool Pass(TNode node) { if (node.Name != _nodeName) return false; if (_propName == null) return true; return node.Attributes[_propName] == _propValue; } } [System.Runtime.CompilerServices.Nullable(0)] public readonly struct NodeList : IEnumerable<TNode>, IEnumerable { private static readonly List<TNode> EmptyList = new List<TNode>(); private readonly TNode _parent; public TNode this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (_parent._childNodes == null || (uint)index >= (uint)_parent._childNodes.Count) ThrowArgumentOutOfRangeException(index); return _parent._childNodes[index]; } } public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { List<TNode> childNodes = _parent._childNodes; if (childNodes == null) return 0; return childNodes.Count; } } internal NodeList(TNode parent) { _parent = parent; } [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowArgumentOutOfRangeException(int index) { throw new ArgumentOutOfRangeException("index", index, "Index was out or range of valid values"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [return: System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })] public List<TNode>.Enumerator GetEnumerator() { return _parent._childNodes?.GetEnumerator() ?? EmptyList.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator<TNode> IEnumerable<TNode>.GetEnumerator() { return GetEnumerator(); } } [System.Runtime.CompilerServices.Nullable(0)] public readonly struct AttributeDictionary { private static readonly Dictionary<string, string> EmptyDictionary = new Dictionary<string, string>(); private readonly TNode _parent; [System.Runtime.CompilerServices.Nullable(2)] public string this[string key] { [MethodImpl(MethodImplOptions.AggressiveInlining)] [return: System.Runtime.CompilerServices.Nullable(2)] get { string value = null; _parent._attributes?.TryGetValue(key, out value); return value; } } public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { Dictionary<string, string> attributes = _parent._attributes; if (attributes == null) return 0; return attributes.Count; } } internal AttributeDictionary(TNode parent) { _parent = parent; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [return: System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1, 1 })] public Dictionary<string, string>.Enumerator GetEnumerator() { return _parent._attributes?.GetEnumerator() ?? EmptyDictionary.GetEnumerator(); } } [System.Runtime.CompilerServices.Nullable(new byte[] { 2, 1 })] private List<TNode> _childNodes; [System.Runtime.CompilerServices.Nullable(new byte[] { 2, 1, 1 })] private Dictionary<string, string> _attributes; public string Name { get; } [System.Runtime.CompilerServices.Nullable(2)] [field: System.Runtime.CompilerServices.Nullable(2)] public string Value { [System.Runtime.CompilerServices.NullableContext(2)] get; [System.Runtime.CompilerServices.NullableContext(2)] set; } public bool ValueIsCDATA { get; set; } public AttributeDictionary Attributes => new AttributeDictionary(this); public NodeList ChildNodes => new NodeList(this); [System.Runtime.CompilerServices.Nullable(2)] public TNode FirstChild { [System.Runtime.CompilerServices.NullableContext(2)] get { List<TNode> childNodes = _childNodes; if (childNodes == null || childNodes.Count != 0) return ChildNodes[0]; return null; } } public string OuterXml { get { using (StringWriter stringWriter = new StringWriter()) { using (XmlWriter writer = XmlWriter.Create(stringWriter, XmlExtensions.FragmentWriterSettings)) WriteTo(writer); return stringWriter.ToString(); } } } public TNode(string name) { Name = name; } public TNode(string name, [System.Runtime.CompilerServices.Nullable(2)] string value) : this(name, value, false) { } public TNode(string name, [System.Runtime.CompilerServices.Nullable(2)] string value, bool valueIsCDATA) : this(name) { Value = XmlExtensions.EscapeInvalidXmlCharacters(value); ValueIsCDATA = valueIsCDATA; } public static TNode FromXml(string xmlText) { using (StringReader input = new StringReader(xmlText)) using (XmlTextReader xmlTextReader = new XmlTextReader(input) { Normalization = false }) { xmlTextReader.MoveToContent(); TNode tNode = null; TNode tNode2 = null; Stack<TNode> stack = new Stack<TNode>(); while (!xmlTextReader.EOF) { if (xmlTextReader.NodeType == XmlNodeType.Element) { TNode item = tNode2; tNode2 = new TNode(xmlTextReader.Name, xmlTextReader.Value); if (tNode == null) { tNode = tNode2; stack.Push(tNode); } else { if (xmlTextReader.Depth > stack.Count) stack.Push(item); TNode tNode3 = stack.Peek(); tNode3.AddChildNode(tNode2); } int attributeCount = xmlTextReader.AttributeCount; if (attributeCount > 0) { for (int i = 0; i < attributeCount; i++) { xmlTextReader.MoveToNextAttribute(); tNode2.AddAttribute(xmlTextReader.Name, xmlTextReader.Value); } } } else if (xmlTextReader.NodeType == XmlNodeType.EndElement) { if (xmlTextReader.Depth < stack.Count) stack.Pop(); } else if (xmlTextReader.NodeType == XmlNodeType.Text) { tNode2.Value = xmlTextReader.Value; } else if (xmlTextReader.NodeType == XmlNodeType.CDATA) { tNode2.Value = xmlTextReader.Value; tNode2.ValueIsCDATA = true; } xmlTextReader.Read(); } if (tNode == null) throw new ArgumentException("Could not extract root element from " + xmlText); return tNode; } } public TNode AddElement(string name) { TNode tNode = new TNode(name); AddChildNode(tNode); return tNode; } public TNode AddElement(string name, string value) { TNode tNode = new TNode(name, value); AddChildNode(tNode); return tNode; } public TNode AddElementWithCDATA(string name, string value) { TNode tNode = new TNode(name, value, true); AddChildNode(tNode); return tNode; } public void AddChildNode(TNode node) { if (_childNodes == null) _childNodes = new List<TNode>(); _childNodes.Add(node); } public void InsertChildNode(int index, TNode node) { if (_childNodes == null) _childNodes = new List<TNode>(); _childNodes.Insert(index, node); } public void AddAttribute(string name, string value) { if (_attributes == null) _attributes = new Dictionary<string, string>(); _attributes.Add(name, XmlExtensions.EscapeInvalidXmlCharacters(value)); } [return: System.Runtime.CompilerServices.Nullable(2)] public TNode SelectSingleNode(string xpath) { List<TNode> list = SelectNodes(xpath); if (list.Count <= 0) return null; return list[0]; } public List<TNode> SelectNodes(string xpath) { List<TNode> list = new List<TNode>(); list.Add(this); return ApplySelection(list, xpath); } public void WriteTo(XmlWriter writer) { writer.WriteStartElement(Name); foreach (KeyValuePair<string, string> attribute in Attributes) { writer.WriteAttributeString(attribute.Key, attribute.Value); } if (Value != null) { if (ValueIsCDATA) writer.WriteCDataSafe(Value); else writer.WriteString(Value); } NodeList childNodes = ChildNodes; int count = childNodes.Count; for (int i = 0; i < count; i++) { childNodes = ChildNodes; childNodes[i].WriteTo(writer); } writer.WriteEndElement(); } private static TNode FromXml(XmlNode xmlNode) { TNode tNode = new TNode(xmlNode.Name, xmlNode.InnerText); if (xmlNode.Attributes != null) { foreach (XmlAttribute attribute in xmlNode.Attributes) { tNode.AddAttribute(attribute.Name, attribute.Value); } } foreach (XmlNode childNode in xmlNode.ChildNodes) { if (childNode.NodeType == XmlNodeType.Element) tNode.AddChildNode(FromXml(childNode)); } return tNode; } private static List<TNode> ApplySelection(List<TNode> nodeList, string xpath) { Guard.ArgumentNotNullOrEmpty(xpath, "xpath"); if (xpath[0] == '/') throw new ArgumentException("XPath expressions starting with '/' are not supported", "xpath"); if (xpath.IndexOf("//", StringComparison.Ordinal) >= 0) throw new ArgumentException("XPath expressions with '//' are not supported", "xpath"); string xpath2 = xpath; string text = null; int num = xpath.IndexOf('/'); if (num >= 0) { xpath2 = xpath.Substring(0, num); text = xpath.Substring(num + 1); } List<TNode> list = new List<TNode>(); NodeFilter nodeFilter = new NodeFilter(xpath2); int count = nodeList.Count; for (int i = 0; i < count; i++) { TNode tNode = nodeList[i]; NodeList childNodes = tNode.ChildNodes; int count2 = childNodes.Count; for (int j = 0; j < count2; j++) { childNodes = tNode.ChildNodes; TNode tNode2 = childNodes[j]; if (nodeFilter.Pass(tNode2)) list.Add(tNode2); } } if (text == null) return list; return ApplySelection(list, text); } } }