Converting from String to <T>

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>
71750 次浏览

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"); // = 12
GetValue<DateTime>("12/12/98");

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.

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.

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.

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>");
}