I'm having problem deserializing previously serialized XML.
My class is generated from .xsd by xsd.exe utility. I have no influence on the structure of .xsd as it was issued by government, and used to standardize communication...
The problem is, when I create an object, set some properties on it, then serialize it using XmlSerializer, and then deserialize it back, I do not get the same "contents" as I started with. Some of the elements from XML deserialize in "Any" property instead of properties from which these elements were serialized in a first place.
Perhaps a clumsy explanation, but I've created a sample project that reproduces my issue.
Sample project can be found here.
Edit:
Ok, here is some sample code. Unfortunately I can't paste everything here, because file generated by xsd.exe is over 4000 lines long. But everything required is in linked file.
My test console app:
static void Main(string[] args)
{
Pismeno pismeno = new Pismeno();
#region Build sample content
pismeno.Sadrzaj = new SadrzajTip();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml("<SomeXml xmlns=\"http://some.namespace.com/\">Some content goes here</SomeXml>");
pismeno.Sadrzaj.Item = xmlDoc.DocumentElement;
pismeno.Prilog = new PismenoPrilog[1];
pismeno.Prilog[0] = new PismenoPrilog();
pismeno.Prilog[0].VrijemeNastanka = DateTime.Now.ToString("s");
XmlDocument xmlTitle = new XmlDocument();
xmlTitle.LoadXml("<Title xmlns=\"http://some.namespace.com/\">Test title 1</Title>");
pismeno.Prilog[0].Any = new XmlElement[1];
pismeno.Prilog[0].Any[0] = xmlTitle.DocumentElement;
pismeno.Prilog[0].Sadrzaj = new SadrzajTip();
EAdresaTip eat = new EAdresaTip();
eat.URL = "http://www.example.com/testfile.doc";
pismeno.Prilog[0].Sadrzaj.Item = eat;
#endregion
// Serialize object, and then deserialize it again
string pismenoSer = Serialize(pismeno);
Pismeno pismeno2 = Deserialize<Pismeno>(pismenoSer);
// Objects to compare. "source" has source.Sadrzaj and source.Prilog properties set
// "shouldBeTheSameAsSource" has shouldBeTheSameAsSource.Any property set
Pismeno source = pismeno;
Pismeno shouldBeTheSameAsSource = pismeno2;
}
public static string Serialize(object o)
{
string ret = null;
using (var stream = new MemoryStream())
{
XmlWriter xw = new XmlTextWriter(stream, Encoding.UTF8) { Formatting = Formatting.Indented };
new XmlSerializer(o.GetType()).Serialize(xw, o);
stream.Flush();
stream.Seek(0, SeekOrigin.Begin);
ret = (new StreamReader(stream, Encoding.UTF8)).ReadToEnd();
}
return ret;
}
public static T Deserialize<T>(string xml)
{
return (T)new XmlSerializer(typeof(T)).Deserialize(XmlReader.Create(new StringReader(xml)));
}
Related
I sent an XML post to demo API and the response comes back as a stream of XML something like this:
API=3CProductData&XML=%3CProductData+Name%3D%22NameTest%22%3E%0D%0A++%3CId%3EXXXXXXXXX%3C%2FId%3E%0D%0A%3C%2FProductData%3E
I'm guessing this is what stream look like and my goal is to take that response and store it inside a new ProductData Object here is what I have done so far:
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
if (response.StatusCode == HttpStatusCode.OK)
{
// as an xml: deserialise into your own object or parse as you wish
StreamReader respStream = new StreamReader(response.GetResponseStream(), System.Text.Encoding.Default);
string receivedResponse = respStream.ReadToEnd();
XmlSerializer x = new XmlSerializer(typeof(ProductData));
ProductData product = (ProductData) x.Deserialize(new StringReader(receivedResponse));
Console.WriteLine("Node1: " + product.Id.ToString());
Console.WriteLine("Node2: " + product.Name);
Console.ReadKey();
}
The Error Comes back with System.InvalidOperationException: 'There is an error in XML document (0, 0).'
XmlException: Root element is missing.
Here are two different solutions.
public ProductData TestFunction()
{
ProductData result = new ProductData();
string apiResponse = "API=3CProductData&XML=%3CProductData+Name%3D%22NameTest%22%3E%0D%0A++%3CId%3EXXXXXXXXX%3C%2FId%3E%0D%0A%3C%2FProductData%3E";
string xml = HttpUtility.UrlDecode(apiResponse.Substring(apiResponse.IndexOf("XML=") + 4));
XmlDocument document = new XmlDocument();
document.LoadXml(xml);
XmlNode newNode = document.DocumentElement;
// Name is actually an attribute on the ProductData
result.Name = ((XmlAttribute)newNode.Attributes["Name"]).InnerText;
// Id is an actual node
result.ID = ((XmlNode)newNode.FirstChild).InnerText;
using (TextReader reader = new StringReader(xml))
{
var serializer = new XmlSerializer(typeof(ProductData));
result = (ProductData)serializer.Deserialize(reader);
}
return result;
}
[Serializable]
[XmlRoot("ProductData")]
public class ProductData
{
[XmlElement("Id")]
public string ID { get; set; }
[XmlAttribute("Name")]
public string Name { get; set; }
}
There is one very fragile part of this code, and I didn't spend a lot of time trying to do handle it. The XML is not really well-formed in my opinion, so you're going to have to substring after the XML= which is why I added the +4 at the end of that. Probably a smoother way to do it, but again the issue is really with converting the XML. Since the XML is really simple, you can just target the values via SelectSingleNode. If you want to go the StreamReader route, you need to make sure your class/properties have the attributes set up (i.e. [XmlRoot("Productdata")])
You must remove the part API=3CProductData&XML= in your string and then, decode your part XML
Look at this code working :
string strRegex = #"<ProductData Name=""NameTest"">\r\n <Id>XXXXXXXXX</Id>\r\n</ProductData>";
ProductData result = null;
using (TextReader reader = new StringReader(strRegex))
{
var serializer = new XmlSerializer(typeof(ProductData));
result = (ProductData)serializer.Deserialize(reader);
}
following is the part of my xml serializing object.
private decimal tOAMOUNTField;
public decimal TOAMOUNT
{
get
{
return this.tOAMOUNTField;
}
set
{
this.tOAMOUNTField = value;
}
}
XmlSerializer xsSubmit = new XmlSerializer(typeof(MyClassObject));
var entity = new Myobject();
entity .TOAMOUNT = 2.22M;
using (StringWriter sww = new StringWriter())
{
using (XmlWriter writer = XmlWriter.Create(sww))
{
// sww.WriteLine(#"<?xml version=""1.0"" encoding=""UTF-8""?>");
xsSubmit.Serialize(writer , entity);
output = sww.ToString();
}
}
above mention "ToAmount" Property is not serializing in XML
Kindly point out the mistake because all other properties are serializing
You are initializing a XmlSerializer object with type MyClassObject.
XmlSerializer xsSubmit = new XmlSerializer(typeof(MyClassObject));
But are serializing a 'Myobject()' object.
Try changing it in:
XmlSerializer xsSubmit = new XmlSerializer(typeof(Myobject));
I have that XML file which looks like this:
<?xml version="1.0" encoding="utf-8"?>
<backupatmail>
<backup id="0">
<foldername>TestFolder</foldername>
<backupdate>09/10/2015</backupdate>
<comment>Sample comment text is here</comment>
<numberofparts>7</numberofparts>
<lastsucceed>Test.007</lastsucceed>
</backup>
<backup id="1">
<foldername>TestFolder2</foldername>
<backupdate>09/10/2015</backupdate>
<comment>Sample comment text is here</comment>
<numberofparts>15</numberofparts>
<lastsucceed>Test.015</lastsucceed>
</backup>
</backupatmail>
Now, I want to append new node(?):
<backup id="999">
<foldername>testing1</foldername>
<backupdate>99/99/9999</backupdate>
</backup>
I wrote following code:
public static void AddBackupToXML()
{
XmlDocument doc = new XmlDocument();
doc.Load(GlobalSettings.appDefaultFolder + "backups.xml");
XmlElement backupNodeNew = doc.CreateElement("backup");
XmlAttribute backupId = doc.CreateAttribute("id");
backupId.Value = "999";
backupNodeNew.Attributes.Append(backupId);
XmlNode nodeTitle = doc.CreateElement("foldername");
nodeTitle.InnerText = "testing1";
XmlNode nodeUrl = doc.CreateElement("backupdate");
nodeUrl.InnerText = "99/99/9999";
backupNodeNew.AppendChild(nodeTitle);
backupNodeNew.AppendChild(nodeUrl);
doc.DocumentElement.AppendChild(backupNodeNew);
doc.Save(GlobalSettings.appDefaultFolder + "backups.xml");
}
Is there any shorten way to do that and also keep simplicity at beginners level?
I will recommend you to use LINQ to XML. It has more simple API for working with XML and your code will look like
var file_name = GlobalSettings.appDefaultFolder + "backups.xml";
XDocument xdoc = XDocument.Load(file_name);
var backup999 = new XElement("backup",
new XAttribute("id", 999),
new XElement("foldername", "testing1"),
new XElement("backupdate", "99/99/9999")
);
xdoc.Root.Add(backup999);
xdoc.Save(file_name);
How I did it plenty of times, using copy special feature of visual studio http://blog.codeinside.eu/2014/09/08/Visual-Studio-2013-Paste-Special-JSON-And-Xml/ I generated the classed describing the xml structure, handle it really clean and easy from code and transfomed when required with System.Xml.Serialization https://support.microsoft.com/en-us/kb/815813 in xml doc or whatever was the need.
The same way from xml to c# object https://msdn.microsoft.com/en-us/library/fa420a9y(v=vs.110).aspx.
My personal preference is to not use the xml api directly. Rather, use the model object then serialize and de-serialize it. It's much cleaner in my opinion and the code to do what you actually need is only 3 lines once your have the generic serializer in place.
What you can do is de-serialize your object into an actual "backupatmail" object and then call backupatmail.BackUpItems.Add(your new item here) and then deserialize it back into a string.
This approach is slower than using LinqToXml during runtime but most of the time a extra millisecond or more isn't a big deal.
Here's the serialization/ deserialization code.
using System.Xml;
using System.Xml.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
public class SerializationUtils
{
public static T Deserialize<T>(string data)
{
XmlSerializer objSerializer = new XmlSerializer(typeof(T));
using (var reader = new StringReader(data))
{
return (T)objSerializer.Deserialize(reader);
}
}
public static string Serialize<T>(T obj)
{
XmlSerializer objSerializer = new XmlSerializer(typeof(T));
XmlSerializerNamespaces emptyNamespaces = new XmlSerializerNamespaces(new [] { XmlQualifiedName.Empty });
var settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
settings.ConformanceLevel = ConformanceLevel.Auto;
#if DEBUG
settings.Indent = true;
#else
settings.Indent = false;
#endif
using (var stream = new StringWriter())
using (var writer = XmlWriter.Create(stream, settings))
{
objSerializer.Serialize(writer, obj, emptyNamespaces);
return stream.ToString();
}
}
}
and for your use.
string mailItems = GetXmlStringFromSomewhere();
var objMail = SerializationUtils.Deserialize<BackUpMail>(mailItems);
var newItem = new BackUp() { FolderName = "testing1", BackupDate = DateTime.Now};
objMail.BackUpItems.Add(newItem);
var strMailWAddedItem = SerializationUtils.Serialize(objMail);
Here are your entity classes.
public class BackUpMail
{
public List<BackUp> BackUpItems {get;set;}
}
public class BackUp
{
[XmlAttribute("id")]
public string ID {get;set}
[XmlElement("foldername")]
public string FolderName {get;set;}
[XmlElement("backupdate")]
public DateTime BackupDate {get;set;}
[XmlElement("comment")]
public string Comment {get;set}
[XmlElement("numberofparts")]
public string NumberOfParts {get;set}
[XmlElement("lastsucceed")]
public string LastSucceed {get;set}
}
Since you have the code, just parameterize it.
public static void someOtherMethod(){
XmlDocument doc = new XmlDocument();
doc.Load(GlobalSettings.appDefaultFolder + "backups.xml");
// some loop..
doc.DocumentElement.AppendChild(createNode(id[i], foldername[i], backupdate[i]));
doc.Save(GlobalSettings.appDefaultFolder + "backups.xml");
}
public static void createNode(string id, string foldername, string backupdate)
{
XmlElement backupNodeNew = doc.CreateElement("backup");
XmlAttribute backupId = doc.CreateAttribute("id");
backupId.Value = id;
backupNodeNew.Attributes.Append(backupId);
XmlNode nodeTitle = doc.CreateElement("foldername");
nodeTitle.InnerText = foldername;
XmlNode nodeUrl = doc.CreateElement("backupdate");
nodeUrl.InnerText = backupdate;
backupNodeNew.AppendChild(nodeTitle);
backupNodeNew.AppendChild(nodeUrl);
return backupNodeNew;
}
I have a library that contains a .NET type. The library has its own configuration (annotations etc) for serializing the type to XML, and I do not have the source code.
No matter what I do, I could not manage to add the prefix I want to add to the root element of the output XML. Using XmlSerializerNamespaces made no difference. Here is a snippet that shows my code at the moment:
var comp = row[0] as LibraryType;
var ser = new XmlSerializer(comp.GetType());
var strWriter = new StringWriter();
var xmlWriter = XmlWriter.Create(strWriter);
ser.Serialize(xmlWriter, comp);
string serXml = strWriter.ToString();
Is there a way in configure XMLSerializer to create an xlm output for root such as
<lt:LibraryType ....
instead of the current
<LibraryType .....
I'm getting ?
Lets say that your library type looks like
public class Foo
{
public int i { get; set; }
}
public class Bar
{
public Foo Foo { get; set; }
}
then serialization should look like
var comp = new Bar {Foo = new Foo()};
var overrides = new XmlAttributeOverrides();
overrides.Add(typeof (Bar), new XmlAttributes {XmlRoot = new XmlRootAttribute {Namespace = "http://tempuri.org"}});
// in case you want to remove prefix from members
var emptyNsAttribute = new XmlAttributes();
emptyNsAttribute.XmlElements.Add(new XmlElementAttribute { Namespace = "" });
overrides.Add(typeof(Bar), "Foo", emptyNsAttribute);
// if you actual library type contains more members, then you have to list all of them
var ser = new XmlSerializer(comp.GetType(), overrides);
var strWriter = new StringWriter();
var xmlWriter = XmlWriter.Create(strWriter);
var ns = new XmlSerializerNamespaces();
ns.Add("lt", "http://tempuri.org");
ser.Serialize(xmlWriter, comp, ns);
string serXml = strWriter.ToString();
and the output will be
<?xml version="1.0" encoding="utf-16"?><lt:Bar xmlns:lt="http://tempuri.org"><Foo><i>0</i></Foo></lt:Bar>
Anybody have a good example how to deep clone a WPF object, preserving databindings?
The marked answer is the first part.
The second part is that you have to create an ExpressionConverter and inject it into the serialization process. Details for this are here:
http://www.codeproject.com/KB/WPF/xamlwriterandbinding.aspx?fid=1428301&df=90&mpp=25&noise=3&sort=Position&view=Quick&select=2801571
The simplest way that I've done it is to use a XamlWriter to save the WPF object as a string. The Save method will serialize the object and all of its children in the logical tree. Now you can create a new object and load it with a XamlReader.
ex:
Write the object to xaml (let's say the object was a Grid control):
string gridXaml = XamlWriter.Save(myGrid);
Load it into a new object:
StringReader stringReader = new StringReader(gridXaml);
XmlReader xmlReader = XmlReader.Create(stringReader);
Grid newGrid = (Grid)XamlReader.Load(xmlReader);
In .NET 4.0, the new xaml serialization stack makes this MUCH easier.
var sb = new StringBuilder();
var writer = XmlWriter.Create(sb, new XmlWriterSettings
{
Indent = true,
ConformanceLevel = ConformanceLevel.Fragment,
OmitXmlDeclaration = true,
NamespaceHandling = NamespaceHandling.OmitDuplicates,
});
var mgr = new XamlDesignerSerializationManager(writer);
// HERE BE MAGIC!!!
mgr.XamlWriterMode = XamlWriterMode.Expression;
// THERE WERE MAGIC!!!
System.Windows.Markup.XamlWriter.Save(this, mgr);
return sb.ToString();
There are some great answers here. Very helpful. I had tried various approaches for copying Binding information, including the approach outlined in http://pjlcon.wordpress.com/2011/01/14/change-a-wpf-binding-from-sync-to-async-programatically/ but the information here is the best on the Internet!
I created a re-usable extension method for dealing with InvalidOperationException “Binding cannot be changed after it has been used.” In my scenario, I was maintaining some code somebody wrote, and after a major DevExpress DXGrid framework upgrade, it no longer worked. The following solved my problem perfectly. The part of the code where I return the object could be nicer, and I will re-factor that later.
/// <summary>
/// Extension methods for the WPF Binding class.
/// </summary>
public static class BindingExtensions
{
public static BindingBase CloneViaXamlSerialization(this BindingBase binding)
{
var sb = new StringBuilder();
var writer = XmlWriter.Create(sb, new XmlWriterSettings
{
Indent = true,
ConformanceLevel = ConformanceLevel.Fragment,
OmitXmlDeclaration = true,
NamespaceHandling = NamespaceHandling.OmitDuplicates,
});
var mgr = new XamlDesignerSerializationManager(writer);
// HERE BE MAGIC!!!
mgr.XamlWriterMode = XamlWriterMode.Expression;
// THERE WERE MAGIC!!!
System.Windows.Markup.XamlWriter.Save(binding, mgr);
StringReader stringReader = new StringReader(sb.ToString());
XmlReader xmlReader = XmlReader.Create(stringReader);
object newBinding = (object)XamlReader.Load(xmlReader);
if (newBinding == null)
{
throw new ArgumentNullException("Binding could not be cloned via Xaml Serialization Stack.");
}
if (newBinding is Binding)
{
return (Binding)newBinding;
}
else if (newBinding is MultiBinding)
{
return (MultiBinding)newBinding;
}
else if (newBinding is PriorityBinding)
{
return (PriorityBinding)newBinding;
}
else
{
throw new InvalidOperationException("Binding could not be cast.");
}
}
}
How about:
public static T DeepClone<T>(T from)
{
using (MemoryStream s = new MemoryStream())
{
BinaryFormatter f = new BinaryFormatter();
f.Serialize(s, from);
s.Position = 0;
object clone = f.Deserialize(s);
return (T)clone;
}
}
Of course this deep clones any object, and it might not be the fastest solution in town, but it has the least maintenance... :)