Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

Using Extension Methods To Avoid XML Problems

, 10 Jul 2014
Rate this:
Please Sign up or sign in to vote.
Extension methods can help you avoid sticky situations.

In my current Silverlight3 project at work, I'm retrieving a string of XML data from a database via stored procedure. An object is initialized with this XML data via an Element property (of type XElement), and the following code is executed:
 

public XElement Element
{
    set
    {
        this.MyVar1 = value.Element("MyVar1").Value;
        // this element is missing - exception!!!
        this.MyVar2 = value.Element("MyVar2").Value;
    }
}

 
I discovered that when a database column contains NULL as its value, and the stored procedure will not return the desired/expected XML child element for the row, the code shown above will throw an exception. To combat this, I've written the following extension methods. These methods allow me to "adjust" the data on the fly so that:
 
a) There is always valid data in my object
 
b) Avoids the inevitable exception in the event that an element is missing.
 
Here's the code:
 

public static class ExtensionMethods
{
    //--------------------------------------------------------------------------------
    public static bool Contains(this XElement root, string name)
    {
	bool contains = false;
	foreach (XElement element in root.Elements())
	{
            if (element.Name == name)
	    {
		contains = true;
		break;
	    }
	}
        return contains;
    }
 
    //--------------------------------------------------------------------------------
    public static string GetValue(this XElement root, string name, string defaultValue)
    {
        string value = defaultValue;
        if (root.Contains(name))
        {
            value = root.Element(name).Value;
        }
        return value;
    }
}

 
Now, I can use this code to set the property, and I can stop worrying about the possibility of missing columns:
 

public XElement Element
{
    set
    {
        this.MyVar1 = value.GetValue("MyVar1", "NO VALUE");
        // still missing, but that's okay!
        this.MyVar2 = value.GetValue("MyVar2", "NOVAL");
    }
}

 
Using extension methods allowed me to add functionality and avoid exceptions without adding complexity to the programmer-facing code. Very cool.
 
EDIT ----------
 
Fixed some spelling errors, and reworded the descriptive text a bit.
 
EDIT (01/21/2011) ===============
 
I decided I should probably post ALL of the methods I use. Since I originally posted this tip, I've added overloads for GetValue, and added GetAttribute (and its overloads). The overloads were implemented to avoid the casting that was necessary where the method was being called. The ones that are shown below fill my own requirements, and you can, of course, add more as your needs evolve:
 

//--------------------------------------------------------------------------------
public static string GetValue(this XElement root, string name, string defaultValue)
{
    return (string)root.Elements(name).FirstOrDefault() ?? defaultValue;
}
 
//--------------------------------------------------------------------------------
public static double GetValue(this XElement root, string name, double defaultValue)
{
    string strValue = (string)root.Elements(name).FirstOrDefault() ?? defaultValue.ToString();
    double value;
    if (!double.TryParse(strValue, out value))
    {
        throw new Exception(string.Format("Element {0}: Value retrieved was not a valid double", name));
    }
    return value;
}
 
//--------------------------------------------------------------------------------
public static decimal GetValue(this XElement root, string name, decimal defaultValue)
{
    string strValue = (string)root.Elements(name).FirstOrDefault() ?? defaultValue.ToString();
    decimal value;
    if (!decimal.TryParse(strValue, out value))
    {
        throw new Exception(string.Format("Element {0}: Value retrieved was not a valid decimal", name));
    }
    return value;
}
 
//--------------------------------------------------------------------------------
public static Int32 GetValue(this XElement root, string name, Int32 defaultValue)
{
    string strValue = (string)root.Elements(name).FirstOrDefault() ?? defaultValue.ToString();
    Int32 value;
    if (!Int32.TryParse(strValue, out value))
    {
        throw new Exception(string.Format("Element {0}: Value retrieved was not a valid 32-bit integer", name));
    }
    return value;
}
 
//--------------------------------------------------------------------------------
public static bool GetValue(this XElement root, string name, bool defaultValue)
{
    string strValue = (string)root.Elements(name).FirstOrDefault() ?? defaultValue.ToString();
    bool value;
    if (!bool.TryParse(strValue, out value))
    {
        throw new Exception(string.Format("Element {0}: Value retrieved was not a valid boolean", name));
    }
    return value;
}
 
//--------------------------------------------------------------------------------
public static DateTime GetValue(this XElement root, string name, DateTime defaultValue)
{
    string strValue = (string)root.Elements(name).FirstOrDefault() ?? defaultValue.ToString();
    DateTime value;
    if (!DateTime.TryParse(strValue, out value))
    {
        throw new Exception(string.Format("Element {0}: Value retrieved was not a valid DateTime", name));
    }
    return value;
}
 
//--------------------------------------------------------------------------------
public static string GetAttribute(this XElement root, string name, string defaultValue)
{
    return (string)root.Attributes(name).FirstOrDefault() ?? defaultValue;
}
 
//--------------------------------------------------------------------------------
public static double GetAttribute(this XElement root, string name, double defaultValue)
{
    string strValue = (string)root.Attributes(name).FirstOrDefault() ?? defaultValue.ToString();
    double value;
    if (!double.TryParse(strValue, out value))
    {
        throw new Exception(string.Format("Attribute {0}: Value retrieved was not a valid double", name));
    }
    return value;
}
 
//--------------------------------------------------------------------------------
public static decimal GetAttribute(this XElement root, string name, decimal defaultValue)
{
    string strValue = (string)root.Attributes(name).FirstOrDefault() ?? defaultValue.ToString();
    decimal value;
    if (!decimal.TryParse(strValue, out value))
    {
        throw new Exception(string.Format("Attribute {0}: Value retrieved was not a valid decimal", name));
    }
    return value;
}
 
//--------------------------------------------------------------------------------
public static Int32 GetAttribute(this XElement root, string name, Int32 defaultValue)
{
    string strValue = (string)root.Attributes(name).FirstOrDefault() ?? defaultValue.ToString();
    Int32 value;
    if (!Int32.TryParse(strValue, out value))
    {
        throw new Exception(string.Format("Attribute {0}: Value retrieved was not a valid 32-bit integer", name));
    }
    return value;
}
 
//--------------------------------------------------------------------------------
public static bool GetAttribute(this XElement root, string name, bool defaultValue)
{
    string strValue = (string)root.Attributes(name).FirstOrDefault() ?? defaultValue.ToString();
    bool value;
    if (!bool.TryParse(strValue, out value))
    {
        throw new Exception(string.Format("Attribute {0}: Value retrieved was not a valid boolean", name));
    }
    return value;
}
 
//--------------------------------------------------------------------------------
public static DateTime GetAttribute(this XElement root, string name, DateTime defaultValue)
{
    string strValue = (string)root.Attributes(name).FirstOrDefault() ?? defaultValue.ToString();
    strValue = strValue.ToUpper().Replace("AS OF:", "").Trim();
    DateTime value;
    if (!DateTime.TryParse(strValue, out value))
    {
        throw new Exception(string.Format("Attribute {0}: Value retrieved was not a valid DateTime", name));
    }
    return value;
}

EDIT (07/07/2014) ======================

I've never really been happy with this code, so I refactored it to just two methods using generics:

//--------------------------------------------------------------------------------
public static T GetValue<T>(this XElement root, string name, T defaultValue)
{
    T value = defaultValue;
    string strValue = (string)root.Elements(name).FirstOrDefault() ?? defaultValue.ToString();

    if (value is string)
    {
        return ((T)(object)strValue);
    }
    else
    {
        var tryParse = typeof (T).GetMethod("TryParse", new [] {typeof(string), typeof(T).MakeByRefType()});
        if (tryParse == null)
        {
            throw new InvalidOperationException();
        }
        var parameters = new object[] {strValue, value};
        if ((bool)tryParse.Invoke(null, parameters))
        {
            value = (T)parameters[1];
        }  
    }
    return value;
}

//--------------------------------------------------------------------------------
public static T GetAttribute<T>(this XElement root, string name, T defaultValue)
{
    T value = defaultValue;
    string strValue = (string)root.Attributes(name).FirstOrDefault() ?? defaultValue.ToString();
    if (value is string)
    {
        value = (T)(object)strValue;
    }
    else
    {
        var tryParse = typeof (T).GetMethod("TryParse", new [] {typeof(string), typeof(T).MakeByRefType()});
        if (tryParse == null)
        {
            throw new InvalidOperationException();
        }
        var parameters = new object[] {strValue, value};
        if ((bool)tryParse.Invoke(null, parameters))
        {
            value = (T)parameters[1];
        }
    }
    return value;
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

John Simmons / outlaw programmer
Software Developer (Senior)
United States United States
I've been paid as a programmer since 1982 with experience in Pascal, and C++ (both self-taught), and began writing Windows programs in 1991 using Visual C++ and MFC. In the 2nd half of 2007, I started writing C# Windows Forms and ASP.Net applications, and have since done WPF, Silverlight, WCF, web services, and Windows services.
 
My weakest point is that my moments of clarity are too brief to hold a meaningful conversation that requires more than 30 seconds to complete. Thankfully, grunts of agreement are all that is required to conduct most discussions without committing to any particular belief system.

Comments and Discussions

 
QuestionExplicit cast operators [modified] PinprofessionalRichard Deeming18-Jul-14 5:44 
GeneralRe: Explicit cast operators PinmemberPIEBALDconsult18-Jul-14 6:03 
GeneralRe: Explicit cast operators PinprofessionalRichard Deeming18-Jul-14 6:15 
GeneralRe: Explicit cast operators PinmemberPIEBALDconsult18-Jul-14 6:55 
GeneralMy vote of 5 PinprofessionalCatchExAs10-Jul-14 13:12 
GeneralRe: My vote of 5 PinmemberJohn Simmons / outlaw programmer11-Jul-14 3:16 
QuestionEvolving Code PinmemberJohn Simmons / outlaw programmer7-Jul-14 5:28 
AnswerRe: Evolving Code PinmemberJames Curran7-Jul-14 6:08 
GeneralRe: Evolving Code PinmemberJohn Simmons / outlaw programmer7-Jul-14 6:55 
GeneralRe: Evolving Code PinmemberJames Curran7-Jul-14 8:24 
GeneralRe: Evolving Code PinmemberJohn Simmons / outlaw programmer7-Jul-14 14:30 
GeneralRe: Evolving Code PinmemberJames Curran7-Jul-14 11:04 
GeneralRe: Evolving Code PinmemberJohn Simmons / outlaw programmer7-Jul-14 14:31 
GeneralRe: Evolving Code PinmemberJames Curran7-Jul-14 18:07 
GeneralRe: Evolving Code PinprofessionalBrisingr Aerowing10-Jul-14 13:28 
GeneralRe: Evolving Code [modified] PinprofessionalBrisingr Aerowing10-Jul-14 13:38 
GeneralRe: Evolving Code PinmemberJohn Simmons / outlaw programmer11-Jul-14 3:13 
General"throw new Exception(...)" Not a good idea - calling code w... PinmemberRichard Deeming25-Jan-11 10:37 
GeneralI use a similar pattern for application settings. PinmemberHalLesesne1-Nov-10 16:46 
GeneralGood use of extension methods PinmemberGregory.Gadow26-Apr-10 17:01 
GeneralRe: My vote of 1 PinmemberJohn Simmons / outlaw programmer26-Apr-10 11:48 
GeneralRe: My vote of 1 PinmvpRajesh R Subramanian26-Apr-10 11:53 
GeneralRe: My vote of 1 PinmemberMark Wallace26-Apr-10 20:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140926.1 | Last Updated 10 Jul 2014
Article Copyright 2010 by John Simmons / outlaw programmer
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid