Regarding XML serialization and customization - c#

i am doing xml serialization but i need to customize the output.
my code is here
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml;
namespace Serialize
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Movie movie = new Movie();
movie.Title = "Starship Troopers";
movie.ReleaseDate = DateTime.Parse("11/7/1997");
movie.Rating = 6.9f;
String XmlizedString = null;
MemoryStream memoryStream = new MemoryStream();
XmlSerializer xs = new XmlSerializer(typeof(Movie));
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
xs.Serialize(xmlTextWriter, movie);
memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
XmlizedString = UTF8ByteArrayToString(memoryStream.ToArray());
}
private String UTF8ByteArrayToString(Byte[] characters)
{
UTF8Encoding encoding = new UTF8Encoding();
String constructedString = encoding.GetString(characters);
return (constructedString);
}
}
public class Movie
{
string _Title = "";
DateTime _ReleaseDate;
float _Rating = 0;
[XmlElement("MovieName")]
public string Title
{
get
{
return _Title;
}
set
{
_Title = value;
}
}
[XmlElement("MovieRating")]
public float Rating
{
get
{
return _Rating;
}
set
{
_Rating = value;
}
}
[XmlElement("MovieReleaseDate")]
public DateTime ReleaseDate
{
get
{
return _ReleaseDate;
}
set
{
_ReleaseDate = value;
}
}
}
}
when i run this code then i am getting the output like
<?xml version="1.0" encoding="utf-8"?>
<Movie xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MovieName>Starship Troopers</MovieName>
<MovieRating>6.9</MovieRating>
<MovieReleaseDate>1997-11-07T00:00:00</MovieReleaseDate>
</Movie>
here if u see then u will note few extra info is coming which i don't require
that xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
this info is coming along with Movie tag. so please what i need to change in my code as a result the extra info will not show in the code.
so my code will look like
<?xml version="1.0" encoding="utf-8"?>
<Movie>
<MovieName>Starship Troopers</MovieName>
<MovieRating>6.9</MovieRating>
<MovieReleaseDate>1997-11-07T00:00:00</MovieReleaseDate>
</Movie>
please help with rectification in detail.

I think you'll have to remove them manually, like here:
How to remove all namespaces from XML with C#?

Related

How to create a class which is to be de-serialized from XML

How to create a C# class which must be used to deserialize the XML as given below
<?xml version="1.0" encoding="utf-8"?>
<XML>
<StatusCode>-2</StatusCode>
<Warnings />
<Errors>
<Error> Debtor #2 Invalid Postal Code</Error>
<Error>Invalid lien term</Error>
</Errors>
</XML>
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication110
{
class Program
{
const string INPUT_FILENAME = #"c:\temp\test.xml";
const string OUTPUT_FILENAME = #"c:\temp\test1.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(INPUT_FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(XML));
XML xml = (XML)serializer.Deserialize(reader);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter writer = XmlWriter.Create(OUTPUT_FILENAME, settings);
serializer.Serialize(writer, xml);
}
}
public class XML
{
public int StatusCode { get; set; }
public string Warnings { get; set; }
[XmlArray("Errors")]
[XmlArrayItem("Error")]
public List<string> errors { get; set; }
}
}
To create classes based on XML, copy the xml to the clipboard, then in Visual Studio 2017, choose the menu option: Edit/Paste Special/Paste XML as classes.
Your Class should look like:
public class ErrorClass
{
struct Error
{
public String message;
}
struct Warning
{
public String message;
}
int StatusCode;
List<Error> Errors;
List<Warning> Warnings;
}
Error and Warning struct could contain more items that are not used in the example you posted.

Skipping Invalid Values On Deserialize

Question
Is it possible to skip invalid values up on de-serialization? For example if a user inserted a invalid value inside the xml file.
Class Definition
using Relink.Data.Enum;
using System;
using System.IO;
using System.Xml.Serialization;
using System.ComponentModel;
namespace Relink {
[Serializable]
public class Settings {
internal static XmlSerializer Serializer = new XmlSerializer(typeof(Settings));
public Difficulty Difficulty {
get;
set;
}
public Boolean CaptureMouse {
get;
set;
}
internal void loadDefaults() {
this.Difficulty = Difficulty.Normal;
this.CaptureMouse = false;
}
}
}
Serialization Method
// ...
if(!File.Exists(GameDir + SettingsFile)) {
Settings = new Settings();
Settings.loadDefaults();
TextWriter writer = new StreamWriter(GameDir + SettingsFile);
Settings.Serializer.Serialize(writer, Settings);
writer.Close();
writer.Dispose();
} else {
TextReader reader = new StreamReader(GameDir + SettingsFile);
Settings = (Settings)Settings.Serializer.Deserialize(reader);
}
// ...
XML Content (valid)
<?xml version="1.0" encoding="utf-8"?>
<Settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Difficulty>Normal</Difficulty>
<CaptureMouse>false</CaptureMouse>
</Settings>
XML Content (invalid)
<?xml version="1.0" encoding="utf-8"?>
<Settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Difficulty>Moo</Difficulty>
<CaptureMouse>false</CaptureMouse>
</Settings>
Remarks
I don't want to "reset" the users settings, i just want to skip the invalid stuff and use default values instead. Otherwise i would you a try/catch construct and just re-generate the xml file.
Unfortunately there is no way to suppress the exception inside XmlSerializer when an unknown enum value is encountered. Instead, you will need to create a string-valued property for this purpose, and serialize that instead of the enum-valued property:
[Serializable]
public class Settings
{
internal static XmlSerializer Serializer = new XmlSerializer(typeof(Settings));
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
[XmlElement("Difficulty")]
public string XmlDifficulty
{
get
{
return Difficulty.ToString();
}
set
{
try
{
Difficulty = (Difficulty)Enum.Parse(typeof(Difficulty), value);
}
catch
{
Debug.WriteLine("Invalid difficulty found: " + value);
Difficulty = Difficulty.Normal;
}
}
}
[XmlIgnore]
public Difficulty Difficulty { get; set; }
public Boolean CaptureMouse { get; set; }
internal void loadDefaults()
{
this.Difficulty = Difficulty.Normal;
this.CaptureMouse = false;
}
}

XML Serialization

I want to add multiple record one by one using XML serialization. I have three text boxes and a button which is used to get the data from the user and then serializae in XML file. However, when I add one record on it's own it's fine, but for the another record it declares multiple root elements which I am not able to handle.
I am doing XML serialization and I am getting this error in XML File
**<?xml version="1.0" encoding="utf-8"?>
<sroot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:my-examples:shaping">**
<CustomerId>3a8bb49e-f616-49a5-8ec8-1886881c3042</CustomerId>
<FirstName>HASEEB</FirstName>
<LastName>KHAN</LastName>
<CustomerEmail>SDCFDS</CustomerEmail>
**</sroot><?xml version="1.0" encoding="utf-8"?>
<sroot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:my-examples:shaping">**
<CustomerId>6d2cbe5e-e1fb-4526-9f98-bf396b4ded55</CustomerId>
<FirstName>ammae</FirstName>
<LastName>wdfjk</LastName>
<CustomerEmail>sdkcbnk</CustomerEmail>
</sroot>
As you can see in above XML code that there are multiple root element is written and I am not able to fix that I have a class called customer.cs and the code is written in this class is
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml;
using System.Xml.Serialization;
namespace XmlSearlizeProject.Classes
{
[Serializable]
[XmlRoot(ElementName = "sroot", Namespace = "urn:my-examples:shaping")]
public class Customer
{
string id = default(string);
string firstName = default(string);
string lastName = default(string);
string email = default(string);
public Customer()
{
}
public Customer(string id, string firstName, string lastName, string email)
{
CustomerId = id;
FirstName = firstName;
LastName = lastName;
Email = email;
}
[XmlElement("CustomerId")]
public string CustomerId
{
get
{
return this.id;
}
set
{
this.id = value;
}
}
[XmlElement("FirstName")]
public string FirstName
{
get
{
return this.firstName;
}
set
{
this.firstName = value;
}
}
[XmlElement("LastName")]
public string LastName
{
get
{
return this.lastName;
}
set
{
this.lastName = value;
}
}
[XmlElement("CustomerEmail")]
public string Email
{
get
{
return this.email;
}
set
{
this.email = value;
}
}
}
}
And my C# code is
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml.Serialization;
using System.IO;
using System.Xml;
using System.Text;
namespace XmlSearlizeProject.WebPages
{
public partial class CustomerPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
private void GeneralFunction(Stream xmlStream)
{
//xmlStream.Close();
string customerId = Guid.NewGuid().ToString();
Classes.Customer customer = new Classes.Customer(customerId,this.FirstNameTextBox.Text,this.LastNameTextBox.Text,this.EmailTextBox.Text);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Classes.Customer));
XmlDocument document = new XmlDocument();
document.Load(xmlStream);
XmlElement id = document.CreateElement("Id");
id.InnerText = customerId;
document.DocumentElement.InsertAfter(id, document.DocumentElement.LastChild);
XmlElement firstName = document.CreateElement("rtgr");
firstName.InnerText = customer.FirstName;
document.DocumentElement.InsertAfter(firstName, document.DocumentElement.LastChild);
XmlElement lastName = document.CreateElement("rtgtr");
lastName.InnerText = customer.LastName;
document.DocumentElement.InsertAfter(lastName, document.DocumentElement.LastChild);
XmlElement email = document.CreateElement("grbfr");
email.InnerText = customer.Email;
document.DocumentElement.InsertAfter(email, document.DocumentElement.LastChild);
XmlTextWriter xmlTextWriter = new XmlTextWriter(xmlStream, Encoding.UTF8);
xmlSerializer.Serialize(xmlTextWriter, customer);
xmlStream.Close();
}
private void SerializeCustomer()
{
if (File.Exists(Server.MapPath("~/Customer.xml")))
{
Stream xmlWriterStream = new FileStream(Server.MapPath("~/Customer.xml"), FileMode.Open, FileAccess.ReadWrite);
GeneralFunction(xmlWriterStream);
xmlWriterStream.Close();
}
}
private void DeSerializeCustomer()
{
Stream xmlReaderStream = new FileStream(Server.MapPath("~/Customer.xml"), FileMode.Open, FileAccess.Read, FileShare.Read);
XmlSerializer xmlDeSerializer = new XmlSerializer(typeof(Classes.Customer));
Classes.Customer customer = (Classes.Customer)xmlDeSerializer.Deserialize(xmlReaderStream);
}
protected void SubmitButton_Click(object sender, EventArgs e)
{
//if (!File.Exists(Server.MapPath("~/Customer.xml")))
//{
SerializeCustomer();
//}
//else
//{
// DeSerializeCustomer();
//}
}
}
}
It looks like you're serializing one customer at a time, versus serializing a list/array/collection of customers to the XML file.
Serializing one works because you have 1 root element - Customer. However, when serializing many, you will need to serializing the collection of customers to the XML file.
So then you'll have (example purposes only):
<Customers>
<sroot/>
<sroot/>
</Customers>
A few articles to look at on this:
C# Serializing a Collection of Objects
http://wcode.net/2009/08/serialize-collection-of-object-in-c/
http://www.codeproject.com/KB/cs/objserial.aspx
Define a schema definition (xsd) in the format you want your xml to look like. Then you can use some external tools like xsd2code. This would Auto-generate your "Customer" class to the format of your xsd. (In case if you are doing in manually). Then your xml will match the way you define in your xsd. Give it a try, defining a xsd for your xml is always a better practice.
You could just inherit from list something like this...
[Serializable]
[XmlRoot(ElementName = "sroot", Namespace = "urn:my-examples:shaping")]
public class CustomerList : List<Customer>
{
}
[Serializable]
public class Customer
{
...
}
Example usage...
CustomerList customerList = new CustomerList
{
new Customer
{
FirstName = "John",
LastName = "Doe",
Email = "johndoe#foobar.com",
CustomerId = "123"
},
new Customer
{
FirstName = "Jane",
LastName = "Doe",
Email = "janedoe#foobar.com",
CustomerId = "456"
}
};
Then your method would serialize the list (instance of CustomerList)...
SerializeCustomerList(CustomerList customers)
{
// Do serialize CustomerList instance...
}
Then the resulting XML would look like...
<sroot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:my-examples:shaping">
<Customer>
<CustomerId>123</CustomerId>
<FirstName>John</FirstName>
<LastName>Doe</LastName>
<CustomerEmail>johndoe#foobar.com</CustomerEmail>
</Customer>
<Customer>
<CustomerId>456</CustomerId>
<FirstName>Jane</FirstName>
<LastName>Doe</LastName>
<CustomerEmail>janedoe#foobar.com</CustomerEmail>
</Customer>
</sroot>
Hope it helps!

Unknown attribute xsi:type in XmlSerializer

I am learning XML Serialization and meet an issue, I have two claess
[System.Xml.Serialization.XmlInclude(typeof(SubClass))]
public class BaseClass
{
}
public class SubClass : BaseClass
{
}
I am trying to serialize a SubClass object into XML file, I use blow code
XmlSerializer xs = new XmlSerializer(typeof(Base));
xs.Serialize(fs, SubClassObject);
I noticed Serialization succeed, but the XML file is kind of like
<?xml version="1.0"?>
<BaseClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="SubClass">
...
</Employee>
If I use
XmlSerializer xs = new XmlSerializer(typeof(Base));
SubClassObject = xs.Deserialize(fs) as SubClass;
I noticed it will complain xsi:type is unknown attribute(I registered an event), although all information embedded in the XML was parsed successfully and members in SubClassObject was restored correctly.
Anyone has any idea why there is error in parsing xsi:type and anything I did wrong?
Thanks
Here is the program that I wrote
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
namespace XmlIncludeExample
{
[XmlInclude(typeof(DerivedClass))]
public class BaseClass
{
public string ClassName = "Base Class";
}
public class DerivedClass : BaseClass
{
public string InheritedName = "Derived Class";
}
class Program
{
static void Main(string[] args)
{
string fileName = "Test.xml";
string fileFullPath = Path.Combine(Path.GetTempPath(), fileName);
try
{
DerivedClass dc = new DerivedClass();
using (FileStream fs = new FileStream(fileFullPath, FileMode.CreateNew))
{
XmlSerializer xs = new XmlSerializer(typeof(BaseClass));
xs.Serialize(fs, dc);
}
using (FileStream fs = new FileStream(fileFullPath, FileMode.Open))
{
XmlSerializer xs = new XmlSerializer(typeof(BaseClass));
DerivedClass dc2 = xs.Deserialize(fs) as DerivedClass;
}
}
finally
{
if (File.Exists(fileFullPath))
{
File.Delete(fileFullPath);
}
}
}
}
}
This produced the following xml
<?xml version="1.0" ?>
- <BaseClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="DerivedClass">
<ClassName>Base Class</ClassName>
<InheritedName>Derived Class</InheritedName>
</BaseClass>
And it worked
I got the same error.
I have not a great answer but this is what I have done:
using System;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
namespace HQ.Util.General
{
/// <summary>
/// Save by default as User Data Preferences
/// </summary>
public class XmlPersistence
{
// ******************************************************************
public static void Save<T>(T obj, string path = null) where T : class
{
if (path == null)
{
path = GetDefaultPath(typeof(T));
}
var serializer = new XmlSerializer(typeof(T));
using (TextWriter writer = new StreamWriter(path))
{
serializer.Serialize(writer, obj);
writer.Close();
}
}
// ******************************************************************
public static T Load<T>(string path = null,
Action<object, XmlNodeEventArgs> actionUnknownNode = null,
Action<object, XmlAttributeEventArgs> actionUnknownAttribute = null) where T : class
{
T obj = null;
if (path == null)
{
path = GetDefaultPath(typeof(T));
}
if (File.Exists(path))
{
var serializer = new XmlSerializer(typeof(T));
if (actionUnknownAttribute == null)
{
actionUnknownAttribute = UnknownAttribute;
}
if (actionUnknownNode == null)
{
actionUnknownNode = UnknownNode;
}
serializer.UnknownAttribute += new XmlAttributeEventHandler(actionUnknownAttribute);
serializer.UnknownNode += new XmlNodeEventHandler(actionUnknownNode);
using (var fs = new FileStream(path, FileMode.Open))
{
// Declares an object variable of the type to be deserialized.
// Uses the Deserialize method to restore the object's state
// with data from the XML document. */
obj = (T)serializer.Deserialize(fs);
}
}
return obj;
}
// ******************************************************************
private static string GetDefaultPath(Type typeOfObjectToSerialize)
{
return Path.Combine(AppInfo.AppDataFolder, typeOfObjectToSerialize.Name + ".xml");
}
// ******************************************************************
private static void UnknownAttribute(object sender, XmlAttributeEventArgs xmlAttributeEventArgs)
{
// Correction according to: https://stackoverflow.com/questions/42342875/xmlserializer-warns-about-unknown-nodes-attributes-when-deserializing-derived-ty/42407193#42407193
if (xmlAttributeEventArgs.Attr.Name == "xsi:type")
{
}
else
{
throw new XmlException("UnknownAttribute" + xmlAttributeEventArgs.ToString());
}
}
// ******************************************************************
private static void UnknownNode(object sender, XmlNodeEventArgs xmlNodeEventArgs)
{
// Correction according to: https://stackoverflow.com/questions/42342875/xmlserializer-warns-about-unknown-nodes-attributes-when-deserializing-derived-ty/42407193#42407193
if (xmlNodeEventArgs.Name == "xsi:type")
{
}
else
{
throw new XmlException("UnknownNode" + xmlNodeEventArgs.ToString());
}
}
// ******************************************************************
}
}

How to change XML root name with XML Serialization?

I am trying to change the root name when doing XML serialization with C#.
It always takes the class name and not the name I am trying to set it with.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyTest test = new MyTest();
test.Test = "gog";
List<MyTest> testList = new List<MyTest>()
{
test
};
SerializeToXML(testList);
}
static public void SerializeToXML(List<MyTest> list)
{
XmlSerializer serializer = new XmlSerializer(typeof(List<MyTest>));
TextWriter textWriter = new StreamWriter(#"C:\New folder\test.xml");
serializer.Serialize(textWriter, list);
textWriter.Close();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
[XmlRootAttribute(ElementName = "WildAnimal", IsNullable = false)]
public class MyTest
{
[XmlElement("Test")]
public string Test { get; set; }
}
}
Result
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMyTest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyTest>
<Test>gog</Test>
</MyTest>
</ArrayOfMyTest>
It does not change it to WildAnimal. I am not sure why. I got this from a tutorial.
Edit # Marc
Thanks. I now see what your doing just seems so odd that you have to make a wrapper around it. I have one more question what happens if I wanted to make this format
<root>
<element>
<name></name>
</element>
<anotherElement>
<product></product>
</anotherElement>
</root>
so like a nested elements. Would I have to make a new class for the second part and stick that in the wrapper class too?
In your example, MyTest is not the root; are you trying to rename the array? I would write a wrapper:
[XmlRoot("NameOfRootElement")]
public class MyWrapper {
private List<MyTest> items = new List<MyTest>();
[XmlElement("NameOfChildElement")]
public List<MyTest> Items { get { return items; } }
}
static void Main() {
MyTest test = new MyTest();
test.Test = "gog";
MyWrapper wrapper = new MyWrapper {
Items = { test }
};
SerializeToXML(wrapper);
}
static public void SerializeToXML(MyWrapper list) {
XmlSerializer serializer = new XmlSerializer(typeof(MyWrapper));
using (TextWriter textWriter = new StreamWriter(#"test.xml")) {
serializer.Serialize(textWriter, list);
textWriter.Close();
}
}

Categories