223

I am receiving XML strings over a socket, and would like to convert these to C# objects.

The messages are of the form:

<msg>
   <id>1</id>
   <action>stop</action>
</msg>

How can this be done?

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
Steve
  • 53,375
  • 33
  • 96
  • 141

15 Answers15

308

You need to use the xsd.exe tool which gets installed with the Windows SDK into a directory something similar to:

C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin

And on 64-bit computers:

C:\Program Files (x86)\Microsoft SDKs\Windows\v6.0A\bin

And on Windows 10 computers:

C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin

On the first run, you use xsd.exe and you convert your sample XML into a XSD file (XML schema file):

xsd yourfile.xml

This gives you yourfile.xsd, which in a second step, you can convert again using xsd.exe into a C# class:

xsd yourfile.xsd /c

This should give you a file yourfile.cs which will contain a C# class that you can use to deserialize the XML file you're getting - something like:

XmlSerializer serializer = new XmlSerializer(typeof(msg));
msg resultingMessage = (msg)serializer.Deserialize(new XmlTextReader("yourfile.xml"));

Should work pretty well for most cases.

Update: the XML serializer will take any stream as its input - either a file or a memory stream will be fine:

XmlSerializer serializer = new XmlSerializer(typeof(msg));
MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(inputString));
msg resultingMessage = (msg)serializer.Deserialize(memStream);

or use a StringReader:

XmlSerializer serializer = new XmlSerializer(typeof(msg));
StringReader rdr = new StringReader(inputString);
msg resultingMessage = (msg)serializer.Deserialize(rdr);
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • Thanks for the detailed explanation. In my case, the XML is coming over a socket, and is a string. How would I de-serialize a string instead of an XML file? – Steve Jul 06 '10 at 15:13
  • 5
    @Steve: You can open a StringReader and pass the Deserialize method. StringReader derives from TextReader. – Skurmedel Jul 06 '10 at 15:15
  • Would you prefer your approach to the one Fahad mentioned using Linq? – Steve Jul 06 '10 at 15:18
  • 2
    @Steve: yes, I would - deserializing into an object and being able to poke at the object's properties seems a lot easier than doing a lot of wiggling with XML elements, attributes, child nodes etc. Linq-to-XML is great if the XML is irregular and changes all the time, or not known ahead of time. – marc_s Jul 06 '10 at 15:19
  • This works great! Just one problem that I ran into: The XML I have: ' ' Code outputted for the "Request_Data" tag (since it doesn't have any attributes): '[XmlArrayItemAttribute("Application", typeof(REQUEST_GROUPREQUEST_DATAApplication), Form=XmlSchemaForm.Unqualified)] public REQUEST_GROUPREQUEST_DATAApplication[][] REQUEST_DATA { get; set; }' And the XMLSerializer doesn't seem to like this two dim. array. – Vinay Apr 12 '16 at 17:55
  • 9
    This web site is much easier than the xsd tool IMO: http://xmltocsharp.azurewebsites.net/ – nasch Oct 17 '16 at 16:50
  • @nasch: thanks for the hint - that site wasn't around in 2010 when I answered - but good to know there are other (and possibly easier) options out there! – marc_s Oct 17 '16 at 19:08
  • @marc_s I didn't even notice the age of this question and answer! Still useful though. – nasch Oct 17 '16 at 21:23
  • I used successfully the example with StringReader. – Sharunas Bielskis Dec 06 '16 at 09:09
  • @nasch Phenomenal link. Thank you. The xsd tool produced a large output and ended up not working for me. The Xml2CSharp.com link was more what I was looking for and much tighter/compact. Thanks again. – secretwep Dec 14 '16 at 23:44
  • @marc_s, in my case it is stuck on "Writing file", while the XML isn't that big. Any idea how I can resolve this? – Happy Bird Mar 20 '17 at 11:19
  • I tried using this xsd tool, it generated wrong classes. The **Paste Special** from the other answer worked and it was much simpler. – Alisson Reinaldo Silva Aug 22 '17 at 19:55
  • Updating comments for new versions of VS (2019), here is the Windows 10 path for xsd: C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools. Note that there may be a NETFX 4.7 tools as well. – j.hull Nov 05 '20 at 15:34
  • Another online parser with some configurations https://json2csharp.com/xml-to-csharp. It even writes some code for you. – Kevin Dec 14 '21 at 00:37
271

You have two possibilities.

Method 1. XSD tool


Suppose that you have your XML file in this location C:\path\to\xml\file.xml
  1. Open Developer Command Prompt
    You can find it in Start Menu > Programs > Microsoft Visual Studio 2012 > Visual Studio Tools Or if you have Windows 8 can just start typing Developer Command Prompt in Start screen
  2. Change location to your XML file directory by typing cd /D "C:\path\to\xml"
  3. Create XSD file from your xml file by typing xsd file.xml
  4. Create C# classes by typing xsd /c file.xsd

And that's it! You have generated C# classes from xml file in C:\path\to\xml\file.cs

Method 2 - Paste special


Required Visual Studio 2012+ with .Net Framework >= 4.5 as project target and 'Windows Communication Foundation' individual component installed
  1. Copy content of your XML file to clipboard
  2. Add to your solution new, empty class file (Shift+Alt+C)
  3. Open that file and in menu click Edit > Paste special > Paste XML As Classes
    enter image description here

And that's it!

Usage


Usage is very simple with this helper class:

using System;
using System.IO;
using System.Web.Script.Serialization; // Add reference: System.Web.Extensions
using System.Xml;
using System.Xml.Serialization;

namespace Helpers
{
    internal static class ParseHelpers
    {
        private static JavaScriptSerializer json;
        private static JavaScriptSerializer JSON { get { return json ?? (json = new JavaScriptSerializer()); } }

        public static Stream ToStream(this string @this)
        {
            var stream = new MemoryStream();
            var writer = new StreamWriter(stream);
            writer.Write(@this);
            writer.Flush();
            stream.Position = 0;
            return stream;
        }


        public static T ParseXML<T>(this string @this) where T : class
        {
            var reader = XmlReader.Create(@this.Trim().ToStream(), new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Document });
            return new XmlSerializer(typeof(T)).Deserialize(reader) as T;
        }

        public static T ParseJSON<T>(this string @this) where T : class
        {
            return JSON.Deserialize<T>(@this.Trim());
        }
    }
}

All you have to do now, is:

    public class JSONRoot
    {
        public catalog catalog { get; set; }
    }
    // ...

    string xml = File.ReadAllText(@"D:\file.xml");
    var catalog1 = xml.ParseXML<catalog>();

    string json = File.ReadAllText(@"D:\file.json");
    var catalog2 = json.ParseJSON<JSONRoot>();
Glitch_Doctor
  • 2,994
  • 3
  • 16
  • 30
Damian Drygiel
  • 17,900
  • 4
  • 35
  • 28
  • 7
    cheers. re: method 2, you have to target .net 4.5 else the option's not available. – timB33 Dec 12 '13 at 11:41
  • About the paste special method. Why is it available only in FW4.5? Will I be able to use the generated class in a FW4.0 project, or it won't work? – Itay.B Sep 21 '15 at 09:52
  • 18
    Method 2 is ridiculously useful! Thanks for that. I had no idea that existed. – Dominic Bindley Feb 13 '16 at 04:35
  • 1
    Kudos for the method 2, works like a charm. Great when trying to simply parse a XML programmatically without having to implement boring classes. – Alex Nov 02 '16 at 22:58
  • 1
    You should make "Paste Special" as a first method- it's the simplest one. Limitation ".Net Framework >= 4.5" is not important in 2017. – Michael Freidgeim Dec 31 '16 at 01:29
  • In my situation I tried `Xml2CSharp` online tool, tried using the `xsd.exe` tool, but both generated wrong classes and was not working for my scenario. That **Paste Special worked perfectly**. I had no idea about it....fantastic tool!!! – Alisson Reinaldo Silva Aug 22 '17 at 19:53
  • 2
    The "Paste XML as classes" requires the WCF workload for Visual Studio installed. – Lennart Nov 23 '17 at 07:24
  • Paste XML As Classes working, but the class not good for desalinize if the XML contain complex namespaces – Tarek El-Mallah Aug 09 '19 at 14:24
  • Update answer to have ParseHelpers.ParseXML and ParseHelpers.ParseJSON instead of xml.ParseXML() as xml is just a string. – Alboz Aug 26 '19 at 11:31
57

Try this method to Convert Xml to an object. It is made for exactly what you are doing:

protected T FromXml<T>(String xml)
{
    T returnedXmlClass = default(T);

    try
    {
        using (TextReader reader = new StringReader(xml))
        {
            try
            {
                returnedXmlClass = 
                    (T)new XmlSerializer(typeof(T)).Deserialize(reader);
            }
            catch (InvalidOperationException)
            {
                // String passed is not XML, simply return defaultXmlClass
            }
        }
    }
    catch (Exception ex)
    {
    }

    return returnedXmlClass ;        
}

Call it using this code:

YourStrongTypedEntity entity = FromXml<YourStrongTypedEntity>(YourMsgString);
Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
RJ.
  • 3,173
  • 4
  • 35
  • 44
  • 7
    Got this error xmlns=''> was not expected."}, any idea ? – Prashant Nov 05 '14 at 14:02
  • Problem is, you need to have your class perfectly formed in advance. Maybe a function that outputs a class when given XML? xsd.exe is hit & miss (mostly miss for complex stuff)... – Yumi Koizumi Jun 03 '17 at 18:25
  • 1
    Oh my god I spent hours dealing with .nets xml serializer, and this worked right out of the gate. – christopher clark Nov 17 '17 at 22:16
  • in case of "was not expected" error use solution above with Edit -> Paste Special instead of manually creating classes – Alex Zel Nov 24 '21 at 10:11
13

Simply Run Your Visual Studio 2013 as Administration ... Copy the content of your Xml file.. Go to Visual Studio 2013 > Edit > Paste Special > Paste Xml as C# Classes It will create your c# classes according to your Xml file content.

user2667652
  • 129
  • 1
  • 2
11

Just in case anyone might find this useful:

public static class XmlConvert
{
    public static string SerializeObject<T>(T dataObject)
    {
        if (dataObject == null)
        {
            return string.Empty;
        }
        try
        {
            using (StringWriter stringWriter = new System.IO.StringWriter())
            {
                var serializer = new XmlSerializer(typeof(T));
                serializer.Serialize(stringWriter, dataObject);
                return stringWriter.ToString();
            }
        }
        catch (Exception ex)
        {
            return string.Empty;
        }
    }

    public static T DeserializeObject<T>(string xml)
         where T : new()
    {
        if (string.IsNullOrEmpty(xml))
        {
            return new T();
        }
        try
        {
            using (var stringReader = new StringReader(xml))
            {
                var serializer = new XmlSerializer(typeof(T));
                return (T)serializer.Deserialize(stringReader);
            }
        }
        catch (Exception ex)
        {
            return new T();
        }
    }
}

You can call it using:

MyCustomObject myObject = new MyCustomObject();
string xmlString = XmlConvert.SerializeObject(myObject);
myObject = XmlConvert.DeserializeObject<MyCustomObject>(xmlString);
Rasmus-E
  • 832
  • 1
  • 8
  • 15
9

You can generate class as described above, or write them manually:

[XmlRoot("msg")]
public class Message
{
    [XmlElement("id")]
    public string Id { get; set; }
    [XmlElement("action")]
    public string Action { get; set; }
}

Then you can use ExtendedXmlSerializer to serialize and deserialize.

Instalation You can install ExtendedXmlSerializer from nuget or run the following command:

Install-Package ExtendedXmlSerializer

Serialization:

var serializer = new ConfigurationContainer().Create();
var obj = new Message();
var xml = serializer.Serialize(obj);

Deserialization

var obj2 = serializer.Deserialize<Message>(xml);

This serializer support:

  • Deserialization xml from standard XMLSerializer
  • Serialization class, struct, generic class, primitive type, generic list and dictionary, array, enum
  • Serialization class with property interface
  • Serialization circular reference and reference Id
  • Deserialization of old version of xml
  • Property encryption
  • Custom serializer
  • Support XmlElementAttribute and XmlRootAttribute
  • POCO - all configurations (migrations, custom serializer...) are outside the class

ExtendedXmlSerializer support .NET 4.5 or higher and .NET Core. You can integrate it with WebApi and AspCore.

user1477388
  • 20,790
  • 32
  • 144
  • 264
  • 1
    Excellent post! I updated the code to modernize it according to the documentation https://github.com/wojtpl2/ExtendedXmlSerializer – user1477388 Jul 21 '18 at 11:16
3

You can use xsd.exe to create schema bound classes in .Net then XmlSerializer to Deserialize the string : http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.deserialize.aspx

Skurmedel
  • 21,515
  • 5
  • 53
  • 66
DaveShaw
  • 52,123
  • 16
  • 112
  • 141
3

Simplifying Damian's great answer,

public static T ParseXml<T>(this string value) where T : class
{
    var xmlSerializer = new XmlSerializer(typeof(T));
    using (var textReader = new StringReader(value))
    {
        return (T) xmlSerializer.Deserialize(textReader);
    }
}
Sam Jazz
  • 131
  • 7
3

I have gone through all the answers as at this date (2020-07-24), and there has to be a simpler more familiar way to solve this problem, which is the following.

Two scenarios... One is if the XML string is well-formed, i.e. it begins with something like <?xml version="1.0" encoding="utf-16"?> or its likes, before encountering the root element, which is <msg> in the question. The other is if it is NOT well-formed, i.e. just the root element (e.g. <msg> in the question) and its child nodes only.

Firstly, just a simple class that contains the properties that match, in case-insensitive names, the child nodes of the root node in the XML. So, from the question, it would be something like...

public class TheModel
{
    public int Id { get; set; }
    public string Action { get; set; }
}

The following is the rest of the code...

// These are the key using statements to add.
using Newtonsoft.Json;
using System.Xml;

bool isWellFormed = false;
string xml =  = @"
<msg>
   <id>1</id>
   <action>stop</action>
</msg>
";

var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
if (isWellFormed)
{
    xmlDocument.RemoveChild(xmlDocument.FirstChild); 
    /* i.e. removing the first node, which is the declaration part. 
    Also, if there are other unwanted parts in the XML, 
    write another similar code to locate the nodes 
    and remove them to only leave the desired root node 
    (and its child nodes).*/
}

var serializedXmlNode = JsonConvert.SerializeXmlNode(
            xmlDocument, 
            Newtonsoft.Json.Formatting.Indented, 
            true
            );
var theDesiredObject = JsonConvert.DeserializeObject<TheModel>(serializedXmlNode);
Olumide
  • 368
  • 2
  • 8
2

I know this question is old, but I stumbled into it and I have a different answer than, well, everyone else :-)

The usual way (as the commenters above mention) is to generate a class and de-serialize your xml.

But (warning: shameless self-promotion here) I just published a nuget package, here, with which you don't have to. You just go:

string xml = System.IO.File.ReadAllText(@"C:\test\books.xml");
var book = Dandraka.XmlUtilities.XmlSlurper.ParseText(xml);

That is literally it, nothing else needed. And, most importantly, if your xml changes, your object changes automagically as well.

If you prefer to download the dll directly, the github page is here.

Jim Andrakakis
  • 165
  • 1
  • 2
  • 9
1

Create a DTO as CustomObject

Use below method to convert XML String to DTO using JAXB

private static CustomObject getCustomObject(final String ruleStr) {
    CustomObject customObject = null;
    try {
        JAXBContext jaxbContext = JAXBContext.newInstance(CustomObject.class);
        final StringReader reader = new StringReader(ruleStr);
        Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
        customObject = (CustomObject) jaxbUnmarshaller.unmarshal(reader);
    } catch (JAXBException e) {
        LOGGER.info("getCustomObject parse error: ", e);
    }
    return customObject;
}
Mohit Singh
  • 5,977
  • 2
  • 24
  • 25
0

If you have the xsd of the xml message then you can generate c# classes using the .Net xsd.exe tool.

This .Net classes can then be used to generate the xml.

Amitabh
  • 59,111
  • 42
  • 110
  • 159
0

In addition to the other answers here you can naturally use the XmlDocument class, for XML DOM-like reading, or the XmlReader, fast forward-only reader, to do it "by hand".

Skurmedel
  • 21,515
  • 5
  • 53
  • 66
0

Another way with an Advanced xsd to c# classes generation Tools : xsd2code.com. This tool is very handy and powerfull. It has a lot more customisation than the xsd.exe tool from Visual Studio. Xsd2Code++ can be customised to use Lists or Arrays and supports large schemas with a lot of Import statements.

Note of some features,

  • Generates business objects from XSD Schema or XML file to flexible C# or Visual Basic code.
  • Support Framework 2.0 to 4.x
  • Support strong typed collection (List, ObservableCollection, MyCustomCollection).
  • Support automatic properties.
  • Generate XML read and write methods (serialization/deserialization).
  • Databinding support (WPF, Xamarin).
  • WCF (DataMember attribute).
  • XML Encoding support (UTF-8/32, ASCII, Unicode, Custom).
  • Camel case / Pascal Case support.
  • restriction support ([StringLengthAttribute=true/false], [RegularExpressionAttribute=true/false], [RangeAttribute=true/false]).
  • Support large and complex XSD file.
  • Support of DotNet Core & standard
Dmitry
  • 6,716
  • 14
  • 37
  • 39
-8
public string Serialize<T>(T settings)
{
    XmlSerializer serializer = new XmlSerializer(typeof(T));
    StringWriter outStream = new StringWriter();
    serializer.Serialize(outStream, settings);
    return outStream.ToString();
}
Jim Buck
  • 2,383
  • 23
  • 42
Mandoleen
  • 2,591
  • 2
  • 20
  • 17