views:

384

answers:

5

I really should be able to get this, but I'm just to the point where I think it'd be easier to ask.

In the C# function:

public static T GetValue<T>(String value) where T:new()
{
   //Magic happens here
}

What's a good implementation for the magic? The idea behind this is that I have xml to parse and the desired values are often primitives (bool, int, string, etc.) and this is the perfect place to use generics... but a simple solution is eluding me at the moment.

btw, here's a sample of the xml I'd need to parse

<Items>
    <item>
     <ItemType>PIANO</ItemType>
     <Name>A Yamaha piano</Name>
     <properties>
      <allowUpdates>false</allowUpdates>
      <allowCopy>true</allowCopy>
     </properties> 
    </item>
    <item>
     <ItemType>PIANO_BENCH</ItemType>
     <Name>A black piano bench</Name>
     <properties>
      <allowUpdates>true</allowUpdates>
      <allowCopy>false</allowCopy>
      <url>www.yamaha.com</url>
     </properties>
    </item>
    <item>
     <ItemType>DESK_LAMP</ItemType>
     <Name>A Verilux desk lamp</Name>
     <properties>
      <allowUpdates>true</allowUpdates>
      <allowCopy>true</allowCopy>
      <quantity>2</quantity>
     </properties>
    </item>
</Items>
+7  A: 

I would suggest instead of trying to parse XML yourself, you try to create classes that would deserialize from the XML into the classes. I would strongly recommend following bendewey's answer.

But if you cannot do this, there is hope. You can use Convert.ChangeType.

public static T GetValue<T>(String value)
{
  return (T)Convert.ChangeType(value, typeof(T));
}

And use like so

GetValue<int>("12"); // = 2
GetValue<DateTime>("12/12/98");
Samuel
+1 for XML Serialization to POCO
bendewey
I'll see your +1 and raise you another +1. Although I'm confused by its elegance this smells like using generics for generics sake.
jfar
Lets argue about the aesthetics of GetValue<int>() vs (int)GetValue() :P
Jimmy
Serialization is out of the question for the intended use of this function. We considered Xml <-> poco at first, but it kind of bogs down the app when the xml gets to the >1Mb range. Thus xpath is the way to go.
Saint Domino
A: 

You can start with something roughly like this:

TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
if (converter != null)
{
   return (T)converter.ConvertFrom(value);
}

If you have to parse attributes that are special types, like colors or culture strings or whatnot, you will of course have to build special cases into the above. But this will handle most of your primitive types.

womp
if you use GetConverter(typeof(T)) you don't need the t variable or the new()
Marc Gravell
Yeah I roughed it out really quick. Maybe I'll give it an edit - thanks.
womp
A: 

For this to work correctly, your generic method is going to have to delegate its actual work to a dedicated class.

Something like

private Dictionary<System.Type, IDeserializer> _Deserializers;
    public static T GetValue<T>(String value) where T:new()
    {
       return _Deserializers[typeof(T)].GetValue(value) as T;
    }

where _Deserializers is some kind of dictionary where you register your classes. (obviously, some checking would be required to ensure a deserializer has been registered in the dictionary).

(In that case the where T:new() is useless because your method does not need to create any object.

Denis Troller
You cannot use the as operator on generic types without the where class restriction.
Samuel
woops, didn't know that one. You can still do a regular Cast I presume ?
Denis Troller
+3  A: 

If you decide to go the route of serialization to POCO (Plain old CLR Object), then there are few tools that can help you generate your objects.

  • You can use xsd.exe to generate a .cs file based on your XML Definition
  • There is a new feature in the WCF REST Starter Kit Preview 2, called Paste as Html. This feature is really cool and lets you take a block of HTML thats in your clipboard, then when you paste it into a cs file it automatically converts the xml to the CLR object for serialization.
bendewey
Serialization is out of the question for the intended use of this function. We considered Xml <-> poco at first, but it kind of bogs down the app when the xml gets to the >1Mb range. The boss has said xpath is the way to go
Saint Domino
LINQ to XML is a faster than xpath.
bendewey
See http://en.wikipedia.org/wiki/XML#Processing_files, XPath uses DOM, Linq to XML uses 'pull parsing'
bendewey
remember, some of us are going to be stuck in the .net 2.0 world for a while... :-)
Saint Domino
Ouch, I'm terribly sorry to hear that.
bendewey
A: 

again with the caveat that doing this is probably a bad idea:

class Item 
{
    public string ItemType { get; set; }
    public string Name { get; set; }
}

public static T GetValue<T>(string xml) where T : new()
{
    var omgwtf = Activator.CreateInstance<T>();
    var xmlElement = XElement.Parse(xml);
    foreach (var child in xmlElement.Descendants())
    {
        var property = omgwtf.GetType().GetProperty(child.Name.LocalName);
        if (property != null) 
            property.SetValue(omgwtf, child.Value, null);
    }
    return omgwtf;
}

test run:

static void Main(string[] args)
{
    Item piano = GetValue<Item>(@"
        <Item>
            <ItemType />
            <Name>A Yamaha Piano</Name>
            <Moose>asdf</Moose>
        </Item>");
}
Jimmy