C# Untyped Xml Parsing

Date: 2024-05-06
using System.Xml;
// ...

public static class XmlNodeExtensions
{
    public static XmlNode PathFirstNode(this XmlNode node, string path) => XmlNodeHelpers.PathFirstNode(node, path);
    public static string PathFirstValue(this XmlNode node, string path, string defValue = default) => XmlNodeHelpers.PathFirstValue(node, path, defValue);
    public static T PathFirstValue<T>(this XmlNode node, string path, T defValue = default) => XmlNodeHelpers.PathFirstValue(node, path, defValue);
    public static IEnumerable<XmlNode> PathSelectNodes(this XmlNode node, string path) => XmlNodeHelpers.PathSelectNodes(node, path);
}

public class XmlNodeHelpers
{
    public static XmlNode PathFirstNode(XmlNode n, string path) => n.SelectNodes(path).OfType<XmlNode>().FirstOrDefault();
    public static string PathFirstValue(XmlNode n, string path, string defValue = default) => PathFirstValue<string>(n, path, defValue);
    public static T PathFirstValue<T>(XmlNode n, string path, T defValue = default)
    {
        var value = PathFirstNode(n, path)?.InnerText;
        if (value == null) return defValue;
        if (value is T t) return t;
        if (typeof(T) == typeof(string))
        {
            return (T)(object)$"{value}";
        }
        var basicType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
        try
        {
            return (T)Convert.ChangeType(value, basicType);
        }
        catch { } // ignore value conversion error
        return defValue;
    }

    public static IEnumerable<XmlNode> PathSelectNodes(XmlNode n, string path) => n.SelectNodes(path).OfType<XmlNode>();
}

// usage:
var doc = new XmlDocument();
doc.LoadXml(orderMessagesXml);

var orders = new List<CommonOrder>();
foreach (var orderMessage in doc.PathSelectNodes("/Orders/Order"))
{
    var order = new CommonOrder();
    orders.Add(order);
    var orderType = (OrderType)orderMessage.PathFirstValue("OrderType", 0);
    order.DeliveryDate = GetDateTimeFromInput(message.PathFirstValue("DeliveryDate", null));
    // ...
    
    foreach (var article in orderMessage.PathSelectNodes("Article"))
    {
        var article = new CommonArticle();
        order.Articles.Add(article);
        article.GTIN = article.PathFirstValue("GTIN");
        article.Quantity = article.PathFirstValue<double>("Quantity");
        // ...
    }
}

Build untyped Xml

using System.Xml;
using System.Xml.Linq;

public static IEnumerable<XElement> GetElements(string[,] parameters)
{
    for (int i = 0; i < parameters.GetLength(0); i += 1)
    {
        var key = parameters[i, 0];
        var value = parameters[i, 1];
        if (value == null) continue; // skip null values
        yield return new XElement(key, new XText(value));
    }
}

var doc = new XDocument(new XElement("Orders"));

var order = new XElement("Order",
        GetElements(new string[,] {
        { "OrderType", "55" },
        { "DeliveryDate", DateToStr(deliveryDate) }
        });
        
foreach (var article in articlesList)
{
     var articleLine = new XElement("Article",
         GetElements(new string[,] {
             { "GTIN", article.GTIN },
             { "Quantity", $"{article.Quantity}" }
         })
     );
     
    order.Add(articleLine);
}

doc.Root.Add(order);
var textWriter = new Utf8StringWriter();
doc.Save(textWriter, SaveOptions.None);
return textWriter.ToString();
85060cookie-checkC# Untyped Xml Parsing