0

I have this class which represent a node TestCase in my XML :

public class TestCase
{
    [XmlAttribute("name")]
    public string name { get; set; }
    public string version { get; set; }
    public string verdict { get; set; }
    public string objective { get; set; }
    public string criteria { get; set; }
    public string issue { get; set; }
    public string clientcomments { get; set; }
    public string authoritycomments { get; set; }
    public string sdk { get; set; }   

}

I use XmlNode.SelectSingleNode to fetch a specific node in my XML. For info, there are no duplicate nodes (no nodes with the same name attribute) if it matters.

So far, I have this code :

public static TestCase FetchNode(string NodeName, string Path)
    {
        TestCase testcase = new TestCase();
        string[] attr = { "name", "version", "verdict", "objective", "criteria"
                , "issue", "clientcomments", "authoritycomments", "sdk" };
        string[] attrval = { null, null,null,null,null,null,null,null,null};

        XmlDocument doc = new XmlDocument();
        doc.Load(Path);

        XmlNode node = doc.SelectSingleNode("/TestsList/TestCase[@name='" + NodeName + "']");

        for (var i = 0; i == attr.Length - 1;i++)
        {
            attrval[i] = node[attr[i]].InnerText;
        }

        testcase.name = attrval[0];
        testcase.version = attrval[1];
        testcase.verdict = attrval[2];
        testcase.objective = attrval[3];
        testcase.criteria = attrval[4];
        testcase.issue = attrval[5];
        testcase.clientcomments = attrval[6];
        testcase.authoritycomments = attrval[7];
        testcase.sdk = attrval[8];

        return testcase;

    }

However, this code is not scalable at all, if I change my class structure, I would need to change the function because each element of the class are hardcoded in it.

This is a wide request, but how could I write this function so if I add or remove a string in the class definition of TestCase, I don`t have to change the function FetchNode.

Thank you for your time.

2 Answers 2

1

You could use XmlSerializer.Deserialize

Example:

using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

public class TestCase
{
    [XmlAttribute("name")]
    public string name { get; set; }
    public string version { get; set; }
    public string verdict { get; set; }
    public string objective { get; set; }
    public string criteria { get; set; }
    public string issue { get; set; }
    public string clientcomments { get; set; }
    public string authoritycomments { get; set; }
    public string sdk { get; set; }   
}

public class Program
{
    public const string XML = @"
<TestCase name='TicketName'>
    <name>Jon Nameson</name>
    <version>10.1</version>
    <verdict>High</verdict>
</TestCase>
";

    public static void Main()
    {
        var doc = new XmlDocument();
        doc.LoadXml(XML);

        var node = doc.SelectSingleNode("/TestCase");

        var serializer = new XmlSerializer(typeof(TestCase));

        var testcase = serializer.Deserialize(new StringReader(node.OuterXml)) as TestCase;

        Console.WriteLine(testcase.name);
        Console.WriteLine(testcase.version);
        Console.WriteLine(testcase.verdict);
    }
}

DotNetFiddle

Sign up to request clarification or add additional context in comments.

1 Comment

Worked well without much changes. Thank you.
1

You can deserialize directly from your selected XmlNode by combining XmlSerializer with XmlNodeReader using the following extension method:

public static class XmlNodeExtensions
{
    public static T Deserialize<T>(this XmlNode element, XmlSerializer serializer = null)
    {
        using (var reader = new ProperXmlNodeReader(element))
            return (T)(serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);
    }

    class ProperXmlNodeReader : XmlNodeReader
    {
        // Bug fix from https://stackoverflow.com/questions/30102275/deserialize-object-property-with-stringreader-vs-xmlnodereader
        public ProperXmlNodeReader(XmlNode node)
            : base(node)
        {
        }

        public override string LookupNamespace(string prefix)
        {
            return NameTable.Add(base.LookupNamespace(prefix));
        }
    }
}

This adds an extension method to XmlNode which invokes XmlSerializer to deserialize the selected node to an instance of the generic type T.

Then do:

var testcase = node.Deserialize<TestCase>();

which is identical to:

var testcase = XmlNodeExtensions.Deserialize<TestCase>(node);

In your case the expected root element name of your TestCase class, namely <TestCase>, matches the actual node name. If the node name does not match the expected root element name, you can tell XmlSerializer to expect a different root name by following the instructions in XmlSerializer Performance Issue when Specifying XmlRootAttribute.

3 Comments

Thanks for the detailed response. My c# understanding is not that advanced so I am not sure what exactly the code you've provided me does. Would you mind giving me a brief explanation of the code? Thanks!
@JephGagnon - I added a brief explanation, but I'm not sure what you need to know.
It was more of a general understanding of what you did exactly. The link you provided gives me more information on that. I marked Negorath response as the answer because I can understand what was done, but I will definitely try this method also (and more importantly, understand it). Thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.