How to serialize Dynamics CRM metadata - c#

I am trying to serialize a complete set of Dynamics CRM metdata, using roughly the following code:
RetrieveAllEntitiesRequest metaDataRequest = new RetrieveAllEntitiesRequest()
{
EntityFilters = EntityFilters.All
};
// Execute the request
RetrieveAllEntitiesResponse metaDataResponse = (RetrieveAllEntitiesResponse)service.Execute(metaDataRequest);
entitiesMetadata = metaDataResponse.EntityMetadata;
// Store Metadata for faster usage in the future
Serialize<EntityMetadata[]>(entitiesMetadata, "foobar_metadata.xml");
public static void Serialize<T>(T value, string filePath)
{
if (value == null)
{
return;
}
try
{
var xmlserializer = new XmlSerializer(typeof(T));
var stringWriter = new StringWriter();
using (var writer = new XmlTextWriter(stringWriter) { Formatting = System.Xml.Formatting.Indented })
{
xmlserializer.Serialize(writer, value);
File.WriteAllText(filePath, stringWriter.ToString());
}
}
catch (Exception ex)
{
throw new Exception("An error occurred", ex);
}
}
Yet, some of the metadata classes do not have parameterless constructors. So I get the following exception:
[...] cannot be serialized because it does not have a parameterless constructor
I know that the common advice is to extend the affected classes and give them a parameterless constructor. Yet, this could mean many iterations of trial and error as many classes are involved in the Dynamics CRM metadata.
Is there any other way of serializing/deserializing, which doesn't need a parameterless constructor for each class?
Did somebody go through the effort of extending classes for Dynamics CRM metadata and can share the code?
Is there a possibility to automatically omit any non-serializable classes and only serialize the rest?

I have never manually serialized an entity's metadata. Fortunately, exporting a solution that contains an entity serializes the metadata.
While adding the constructors might allow you to get over the hump with "manual" serializing, you could programmatically create a solution, add all the entities to it, and export it as an unmanaged solution.
The exported solution will be a zip file. All of the entities' metadata will be the cutomizations.xml file. You could then parse that XML and split each entity out to its own file.
Out of curiosity, what is driving the requirement to serialize the metadata? Is it for documentation purposes?

Related

.net property won't deserialize from json to .net object [duplicate]

I searched through similar questions and couldn't find anything that quite matched what i was looking for.
New to C# so bear with me please.
I have some json files that i am deserializing. I want the files to deserialize to objects of the correct type, without having to define the type before hand. Here's my code:
public class loadJson
{
//path of the file location
public void readJson(string path)
{
//array of files at the path location. right now just reading one file
FileInfo[] files = new DirectoryInfo(path).GetFiles("seleniumExample.json").ToArray();
foreach (FileInfo fi in files)
{
dynamic b1 = null;
using (StreamReader file = new StreamReader(fi.FullName))
{
string fileText = file.ReadToEnd();
//Console.WriteLine(fileText);
try
{
b1 = Newtonsoft.Json.JsonConvert.DeserializeObject(fileText);
}
catch(Exception e)
{
Console.WriteLine("ERROR!!!! " + e.ToString());
}
file.Close();
}
}
}
}
I have a bunch of object types that I will be feeding into my program through json files.
I don't want to have to explicitly call b1 a Bid, or a Client, or any other specific predefined class. If I do explicitly call b1 a Bid, it loads all the info just fine and fills out the correct instance variables.
But when I use "dynamic", or general "object", it can't figure it out and just initializes to an "object".
Is there a way to perform generic deserialization and have it create an object of the correct class based on the fields defined in the json file?
Thanks in advance for the help, and i apologize if my question is incredibly unclear. If so, please just let me know how I can help clear up any ambiguity. Thanks again.
Json.NET has the ability to record the .Net object type of polymorphic types during serialization, by using the setting TypeNameHandling = TypeNameHandling.Auto. When the setting is enabled the .Net type of polymorphic objects will appear as a synthetic property called "$type", for instance:
"$type": "Newtonsoft.Json.Samples.Stockholder, Newtonsoft.Json.Tests"
However, the "$type" property is never emitted for the root object if you call the conventional methods JsonConvert.SerializeObject(Object) or JsonSerializer.Serialize(TextWriter, Object). Instead, you must use one of the serialization methods that accepts an "expected" root type, for instance SerializeObject(Object, Type, JsonSerializerSettings) or JsonSerializer.Serialize(TextWriter, Object, Type). Passing typeof(object) as the expected type guarantees the type property will appear:
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto };
var json = JsonConvert.SerializeObject(rootObject, typeof(object), settings);
If you create your JSON files using this setting, the JSON itself will remember the type of object serialized. This type will be used by Json.NET during deserialization, as long as you use set TypeNameHandling to something other than TypeNameHandling.None. e.g.:
var settings = new Newtonsoft.Json.JsonSerializerSettings { TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto };
b1 = Newtonsoft.Json.JsonConvert.DeserializeObject(fileText, settings);
Working sample .Net fiddle here.
Caveats: this way of storing .Net types in JSON is nonstandard. Other serializers such as DataContractJsonSerializer do not process type information in this format.
Note also this caution from the Newtonsoft docs:
TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.
For a discussion of why this may be necessary, see TypeNameHandling caution in Newtonsoft Json, How to configure Json.NET to create a vulnerable web API, and Alvaro Muñoz & Oleksandr Mirosh's blackhat paper https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf.
Deserialize your JSON into the most basic form:
Dictionary<string, object> theData= new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(jsonString);
string baseItemName = (string)theData["baseItem"];
Dictionary<string, object> someNode= (Dictionary<string, object>)theData["something"];
string anything = (string)someNode["anything"];
string nothing = (string)someNode["nothing"];
The call to Deserialize() creates a tree of Dictionary<string, object> that you can traverse at will.

How to deserialize JSON to objects of the correct type, without having to define the type before hand?

I searched through similar questions and couldn't find anything that quite matched what i was looking for.
New to C# so bear with me please.
I have some json files that i am deserializing. I want the files to deserialize to objects of the correct type, without having to define the type before hand. Here's my code:
public class loadJson
{
//path of the file location
public void readJson(string path)
{
//array of files at the path location. right now just reading one file
FileInfo[] files = new DirectoryInfo(path).GetFiles("seleniumExample.json").ToArray();
foreach (FileInfo fi in files)
{
dynamic b1 = null;
using (StreamReader file = new StreamReader(fi.FullName))
{
string fileText = file.ReadToEnd();
//Console.WriteLine(fileText);
try
{
b1 = Newtonsoft.Json.JsonConvert.DeserializeObject(fileText);
}
catch(Exception e)
{
Console.WriteLine("ERROR!!!! " + e.ToString());
}
file.Close();
}
}
}
}
I have a bunch of object types that I will be feeding into my program through json files.
I don't want to have to explicitly call b1 a Bid, or a Client, or any other specific predefined class. If I do explicitly call b1 a Bid, it loads all the info just fine and fills out the correct instance variables.
But when I use "dynamic", or general "object", it can't figure it out and just initializes to an "object".
Is there a way to perform generic deserialization and have it create an object of the correct class based on the fields defined in the json file?
Thanks in advance for the help, and i apologize if my question is incredibly unclear. If so, please just let me know how I can help clear up any ambiguity. Thanks again.
Json.NET has the ability to record the .Net object type of polymorphic types during serialization, by using the setting TypeNameHandling = TypeNameHandling.Auto. When the setting is enabled the .Net type of polymorphic objects will appear as a synthetic property called "$type", for instance:
"$type": "Newtonsoft.Json.Samples.Stockholder, Newtonsoft.Json.Tests"
However, the "$type" property is never emitted for the root object if you call the conventional methods JsonConvert.SerializeObject(Object) or JsonSerializer.Serialize(TextWriter, Object). Instead, you must use one of the serialization methods that accepts an "expected" root type, for instance SerializeObject(Object, Type, JsonSerializerSettings) or JsonSerializer.Serialize(TextWriter, Object, Type). Passing typeof(object) as the expected type guarantees the type property will appear:
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto };
var json = JsonConvert.SerializeObject(rootObject, typeof(object), settings);
If you create your JSON files using this setting, the JSON itself will remember the type of object serialized. This type will be used by Json.NET during deserialization, as long as you use set TypeNameHandling to something other than TypeNameHandling.None. e.g.:
var settings = new Newtonsoft.Json.JsonSerializerSettings { TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto };
b1 = Newtonsoft.Json.JsonConvert.DeserializeObject(fileText, settings);
Working sample .Net fiddle here.
Caveats: this way of storing .Net types in JSON is nonstandard. Other serializers such as DataContractJsonSerializer do not process type information in this format.
Note also this caution from the Newtonsoft docs:
TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.
For a discussion of why this may be necessary, see TypeNameHandling caution in Newtonsoft Json, How to configure Json.NET to create a vulnerable web API, and Alvaro Muñoz & Oleksandr Mirosh's blackhat paper https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf.
Deserialize your JSON into the most basic form:
Dictionary<string, object> theData= new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(jsonString);
string baseItemName = (string)theData["baseItem"];
Dictionary<string, object> someNode= (Dictionary<string, object>)theData["something"];
string anything = (string)someNode["anything"];
string nothing = (string)someNode["nothing"];
The call to Deserialize() creates a tree of Dictionary<string, object> that you can traverse at will.

How do I deserialise xml into a DataContract when the XML has had namespaces removed?

I serialise my wcf requests and responses into XML but to save database space, I strip out all non-essential information, so the result is:
<someObject>
<someValue>10</someValue>
</someObject>
There are more complex nested properties, the above is just an example.
When I try to deserialise, I get an error saying expecting someObject, someNamespace but encountered someObject, ''
byte[] data = System.Text.Encoding.UTF8.GetBytes(xmlString);
stream.Write(data, 0, data.Length);
stream.Position = 0;
DataContractSerializer deserializer = new DataContractSerializer(typeof(T));
return deserializer.ReadObject(stream) as T;
Is there an easy way to solve this? Perhaps by not using a DataContractSerializer?
I know this post is almost a week old, but it may help someone if you've already found a solution.
If you know the structure beforehand, you could use the XmlSerializer class.
string xml = "<someObject>" +
" <someValue>10</someValue>" +
"</someObject>";
using (TextReader reader = new StringReader(xml))
{
XmlSerializer serializer = new XmlSerializer(typeof(someObject));
var obj = serializer.Deserialize(reader);
}
Here's the supporting class to deserialize it into:
[Serializable()]
public class someObject
{
[XmlElement("someValue")]
public string someValue { get; set; }
}
For more complex XML, you'd have to modify the class that you're using. You can nest classes as well as support arrays/lists.

Control XML element name dynamically during serialization

here's my problem to be solved: I've a plugin-structure for multimedia shows that allows to implement media types in external assemblies by subclassing from a base class in my framework. A show holds a list of media types. Shows are loaded and saved in XML using the XmlSerializer. This all works, even with programatic type mapping for plugin MediaTypes.
However, I want to be able to load XML files that contain MediaTypes that are not known, because the plugin isn't available.
For illustration, here is such an XML file:
<MultiMediaShow>
<MediaTypes>
<SomeType />
<SomeType />
<AnotherType />
<UnknownTypeFromPluginNotLoaded />
</MediaTypes>
</MultiMediaShow>
In the above example, I assume 2 "known" MediaTypes SomeType and AnotherType, comming from 2 plugin assemblies. The third type (UnknownTypeFromPluginNotLoaded) is unknown.
I'm already able to deserialize such unknown objects, but struggle with the serialization. In my aplication, I've the following code so far:
// To be implemented / subclassed by plugin assembly types
public abstract class MediaType
{
}
public class UnknownMediaType : MediaType
{
[XmlAnyElement]
public XmlElement[] UnknownChildElements;
[XmlAnyAttribute]
public XmlAttibute[] UnknownAttributes;
[XmlIgnore]
public string XmlTagName; // stores the xml element tag name in the original document
}
[XmlRoot("MultimediaShow")]
public class MultimediaShow
{
public List<MediaType> MediaTypes = new List<MediaType>();
}
When deserializing this with XmlSerializer, I use the event UnknownElement and manually insert an UnknownMediaType element into show.MediaTypes:
void HandleUnknownElements(Show show, List<XmlElementEventArgs> unknownElementEvents, XmlAttributeOverrides overrides)
{
// add a root attribute to UnknownMediaType
XmlAttributes attrs = new XmlAttributes();
XmlRootAttribute xmlRoot = new XmlRootAttribute(e.Element.Name);
attrs.XmlRoot = xmlRoot;
XmlAttributeOverrides o = new XmlAttributeOverrides();
o.Add(typeof(UnknownMediaObject), attrs);
// use a new XmlSerializer and a memory stream for deserializting the object as UnknownMediaType.
XmlSerializer xmlSerializer = new XmlSerializer(typeof(UnknownMediaType), o);
using (MemoryStream memoryStream = new MemoryStream())
{
XmlDocument doc = new XmlDocument();
doc.AppendChild(doc.ImportNode(e.Element, true));
doc.Save(memoryStream);
memoryStream.Position = 0;
try
{
// deserialize the object, store the XML element name and insert it into the chapter
UnknownMediaType t = xmlSerializer.Deserialize(memoryStream) as UnknownMediaObject;
t.XmlTagName = e.Element.Name;
show.MediaTypes.Add(t);
}
catch (Exception exc)
{
System.Diagnostics.Debug.WriteLine(exc.ToString());
//return objectType.IsByRef ? null : Activator.CreateInstance(objectType);
}
}
}
The BIG BIG problem is that such an event doesn't seem to be available when serializing an object. What I get as output (not very surpising) is:
<MultiMediaShow>
<MediaTypes>
<SomeType />
<SomeType />
<AnotherType />
<UnknownMediaType /> // !!!! was 'UnknownTypeFromPluginNotLoaded' !!!!
</MediaTypes>
</MultiMediaShow>
However, this is obviously not the same as what I've deserialized. So the question is, how would I best solve this problem?!?!
All help highly appreciated!!
Cheers,
Felix
UPDATE
I was wondering if it is possible to generate classes programmatically that derive from UnknownMediaType and have the class name taken from the UnknownMediaType.XmlTagName. Or, alternativly, to have an attribute for specifying the XML tag name of a class??
Cheers,
Felix
In fact, I implemented some working solution based on building types dynamically. So far, it's doing what I want. The only downside I see at the moment is that I create them into the current app domain, so I can't unload them (e.g. if a new show is loaded or if the plugins would be made available at runtime).
Here's my code:
void HandleUnknownElements(Show show, List<XmlElementEventArgs> unknownElementEvents, XmlAttributeOverrides overrides)
{
foreach (XmlElementEventArgs e in unknownElementEvents)
{
// (1) Dynamically create a type that simply inherits from UnknownMediaType
AssemblyName assName = new AssemblyName("Show Dynamic Type " + e.Element.Name + " Assembly");
AssemblyBuilder assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.Run);
ModuleBuilder modBuilder = assBuilder.DefineDynamicModule(assBuilder.GetName().Name);
TypeBuilder typeBuilder = modBuilder.DefineType(e.Element.Name, TypeAttributes.Class | TypeAttributes.Public, typeof(UnknownMediaType));
Type objectType = typeBuilder.CreateType();
// (2) Add a root attribute to the type as override
XmlAttributes attrs = new XmlAttributes();
XmlRootAttribute xmlRoot = new XmlRootAttribute(e.Element.Name);
attrs.XmlRoot = xmlRoot;
XmlAttributeOverrides o = new XmlAttributeOverrides();
o.Add(objectType, attrs);
// (3) Use a memory stream for creating a temporary XML document that will be deserialized
using (MemoryStream memoryStream = new MemoryStream())
{
XmlDocument doc = new XmlDocument();
doc.AppendChild(doc.ImportNode(e.Element, true));
doc.Save(memoryStream);
memoryStream.Position = 0;
// (4) Deserialize the object using an XmlSerializer and add it
try
{
XmlSerializer xmlSerializer = new XmlSerializer(objectType, o);
UnknownMediaType t = xmlSerializer.Deserialize(memoryStream) as UnknownMediaType;
show.MediaTypes.Add(t);
}
catch (Exception exc)
{
System.Diagnostics.Debug.WriteLine(exc.ToString());
}
}
}
}
Would be glad if you'd post any issues with this and your concerns...
Cheers,
Felix
See whether you can implement the IXmlSerializable interface for your root class.
From MSDN:
There are two reasons to implement
this interface. The first is to
control how your object is serialized
or deserialized by the XmlSerializer.
For example, you can chunk data into
bytes instead of buffering large data
sets, and also avoid the inflation
that occurs when the data is encoded
using Base64 encoding. To control the
serialization, implement the ReadXml
and WriteXml methods to control the
XmlReader and XmlWriter classes used
to read and write the XML. For an
example of this, see How To: Chunk
Serialized Data.
The second reason is to be able to
control the schema. To enable this,
you must apply the
XmlSchemaProviderAttribute to the
serializable type, and specify the
name of the static member that returns
the schema. See the
XmlSchemaProviderAttribute for an
example.
A class that implements the interface
must have a parameterless constructor.
This is a requirement of the
XmlSerializer class
You can implement a customer serializer. It is more work than using the xmlserializer, but gives you full control. See: http://msdn.microsoft.com/en-us/library/ty01x675(v=vs.80).aspx

Convert XML String to Object

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?
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);
You have two possibilities.
Method 1. XSD tool
Suppose that you have your XML file in this location C:\path\to\xml\file.xml
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
Change location to your XML file directory by typing cd /D "C:\path\to\xml"
Create XSD file from your xml file by typing xsd file.xml
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
Copy content of your XML file to clipboard
Add to your solution new, empty class file (Shift+Alt+C)
Open that file and in menu click Edit > Paste special > Paste XML As Classes
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>();
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);
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.
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);
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.
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
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);
}
}
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);
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.
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;
}
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.
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".
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
public string Serialize<T>(T settings)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
StringWriter outStream = new StringWriter();
serializer.Serialize(outStream, settings);
return outStream.ToString();
}

Categories