Adding Custom Metadata to a Serialized XML Document - .NET - c#

I am currently serializing my XML objects like this:
public static string Serialize<T>(T value) {
XmlSerializer xsSubmit = new XmlSerializer(typeof(T));
using (var sww = new StringWriter())
{
using (XmlTextWriter writer = new XmlTextWriter(sww))
{
xsSubmit.Serialize(writer, value);
}
return sww.ToString();
}
}
However, I want some custom MetaData in my XML document:
<myXML>
<META>
<timeStamp>Sunday, November 11, 2020</timeStamp>
...
</META>
<DATA>
// Serialized Object
</DATA>
</myXML>
Is there anyway I can achieve this without manually building the whole document with something like XDocument, or adding fields to all of my objects?

There are many ways to do it.
For example
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace ConApp
{
public class Data
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Meta
{
[XmlElement("timeStamp")]
public DateTime Timestamp { get; set; }
// other meta properties
}
class Program
{
static void Main()
{
var data = new Data { Id = 5, Name = "Five" };
var meta = new Meta { Timestamp = DateTime.UtcNow };
var result = Serialize(data, meta);
Console.WriteLine(result);
}
public static string Serialize<T>(T value, Meta meta)
{
var metaSerializer = new XmlSerializer(typeof(Meta));
var typeSerializer = new XmlSerializer(typeof(T));
var settings = new XmlWriterSettings { Indent = true };
using (var stringWriter = new StringWriter())
using (var xmlWriter = XmlWriter.Create(stringWriter, settings))
{
xmlWriter.WriteStartElement("myXML");
metaSerializer.Serialize(xmlWriter, meta);
typeSerializer.Serialize(xmlWriter, value);
xmlWriter.WriteEndElement();
xmlWriter.Flush();
return stringWriter.ToString();
}
}
}
}

Related

Serializing Class having property of type object

The Property Value of Type object can be single value of any type like int, string, decimal etc. Or it can be a collection of values like List < int >, List < string > etc.
public class Criteria
{
[XmlElement("IDS")]
public object Value
{
get; set;
}
}
Current Output:
<CriteriaGroup>
<Criteria p2:type="Criteria" xmlns:p2="http://www.w3.org/2001/XMLSchema-instance">
<IDS p2:type="ArrayOfInt">
<int>2610</int>
<int>2452</int>
</IDS>
</Criteria>
<Criteria p2:type="Criteria" xmlns:p2="http://www.w3.org/2001/XMLSchema-instance">
<IDS p2:type="ArrayOfString">
<string>CUMULU1MO</string>
<string>ALLIEDWX2</string>
</IDS>
</Criteria>
</CriteriaGroup>
Expected Output:
How can I achieve below result? Tried decorating public object Value with XmlArray, XmlArrayItem but no luck.
<CriteriaGroup>
<Criteria>
<IDS>
<ID>2610</ID>
<ID>2452</ID>
</IDS>
</Criteria>
<Criteria>
<IDS>
<ID>CUMULU1MO</ID>
<ID>ALLIEDWX2</ID>
</IDS>
</Criteria>
</CriteriaGroup>
Used below method to Serialize.
public static string Serialize<T>(T data)
{
string xmlData = string.Empty;
XmlSerializer ser = new XmlSerializer(typeof(T));
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
var settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
using (StringWriter sw = new StringWriter())
{
using (XmlWriter xw = XmlWriter.Create(sw, settings))
{
ser.Serialize(xw, data, ns);
xmlData = sw.ToString();
}
}
return xmlData;
}
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
CriteriaGroup CriteriaGroup = new CriteriaGroup()
{
Criteria = new List<Criteria>() {
new Criteria() { Value = new string[] { "2610","2452"}
},
new Criteria() { Value = new string[] {"CUMULU1MO", "ALLIEDWX2"}
}
}
};
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter writer = XmlWriter.Create(FILENAME, settings);
XmlSerializer serializer = new XmlSerializer(typeof(CriteriaGroup));
serializer.Serialize(writer, CriteriaGroup);
}
}
public class CriteriaGroup
{
[XmlElement()]
public List<Criteria> Criteria { get; set; }
}
public class Criteria
{
[XmlArray("IDS")]
[XmlArrayItem("ID")]
public string[] Value
{
get;
set;
}
}
}

Converting ordinary string to object

Is it possible to convert ordinary string like:
"Data: {
id: '288dsbshbdas8dsdsb',
data: '2pm'
}"
to:
Data: {
id: '288dsbshbdas8dsdsb',
data: '2pm'
}
Have tried like that:
string input = "Data: {id: '288dsbshbdas8dsdsb', data: '2pm'};
var output = Convert.ChangeType(input, TypeCode.Object);
But this still returns string?
Using NewtonSoft JsonTextWriter, and JsonTextReader You can easly write and read this kind of string.
For writing your must use JsonTextWriter property:
writer.QuoteName = false;
writer.QuoteChar = '\'';
To read to custom configuration is needed.
using System;
using System.IO;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
var objSource= new RootObject{
Data= new Data{
id="123456",
data="FooBar"
}
};
var serializer = new JsonSerializer();
var stringWriter = new StringWriter();
var writer = new JsonTextWriter(stringWriter);
writer.QuoteName = false;
writer.QuoteChar = '\'';
serializer.Serialize(writer, objSource);
var input= stringWriter.ToString();
Console.WriteLine(input);
JsonTextReader reader = new JsonTextReader(new StringReader(input));
var result = serializer.Deserialize<RootObject>(reader);
result.Dump();
}
public class Data
{
public string id { get; set; }
public string data { get; set; }
}
public class RootObject
{
public Data Data { get; set; }
}
}
live demo
Disclaimer: As Jodrell noticed it return a "RootObject".
The writer will return {Data:{id:'123456',data:'FooBar'}}
instead of Data:{id:'123456',data:'FooBar'} Notice the extra {} around the string.
The string manipulation needed to get from one too the other is minor enought.

Error when deserializing

I'm trying to write some code to deserialize an XML file. I've looked around a bit and found something which has led me to the following method:
public static void Deserialize(string filePath)
{
RootObject ro = null;
string path = filePath;
XmlSerializer serializer = new XmlSerializer(typeof(RootObject));
StreamReader reader = new StreamReader(path);
ro = (RootObject) serializer.Deserialize(reader);
reader.Close();
}
But all I get is this error and I'm not sure what's causing it:
There is an error in XML document (2, 2).
The RootObject you see in Deserialize() is this one: I'm new to XMl serializing/deserializing so I'm not sure if I've defined it 100% correctly.
public class RootObject
{
public Services Services { get; set; }
}
public class Services
{
public Service TileMapService { get; set; }
}
public class Service
{
public string Title { get; set; }
public string href { get; set; }
}
I'm using this method to create the XML files in the first place and it seems to work fine:
public static void WriteToXmlFile<T>(string filePath, T objectToWrite) where T : new()
{
TextWriter writer = null;
try
{
var serializer = new XmlSerializer(typeof (T));
writer = new StreamWriter(filePath);
serializer.Serialize(writer, objectToWrite);
}
finally
{
if (writer != null)
{
writer.Close();
}
}
}
It gets me an XML file that looks like this:
<RootObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Services>
<TileMapService>
<Title>Some title</Title>
<href>http://something</href>
</TileMapService>
</Services>
</RootObject>
Your code works fine. The "There is an error in XML document (2, 2)." might be because your actual file (not the one created by WriteToXmlFile<T>) has whitespace at the start, or is using the wrong namespace. Check the .InnerException for more detail, or (perhaps simpler) please post the actual xml file contents. Example of it working fine (along with some recommended tweaks to the two key methods):
static void Main()
{
RootObject obj = new RootObject
{
Services = new Services
{
TileMapService = new Service
{
Title = "abc",
href = "def"
}
}
};
WriteToXmlFile("foo.xml", obj);
var loaded = Deserialize<RootObject>("foo.xml");
var svc = loaded.Services.TileMapService;
System.Console.WriteLine(svc.Title); // abc
System.Console.WriteLine(svc.href); // def
}
public static void WriteToXmlFile<T>(string filePath, T objectToWrite)
{
var serializer = new XmlSerializer(typeof(T));
using (var writer = new StreamWriter(filePath))
{
serializer.Serialize(writer, objectToWrite);
}
}
public static T Deserialize<T>(string filePath)
{
var serializer = new XmlSerializer(typeof(T));
using (var reader = new StreamReader(filePath))
{
return (T)serializer.Deserialize(reader);
}
}

PaymentDetails is not successfully deserialized, returning a null object, because it seems it is expected to have IXmlDeserializable

[XmlRoot("Quote")]
public class Quote
{
[XmlElement("Insurance")]
public InsuranceDetails InsDetails { get; set; }
[XmlElement("Payment")]
public PaymentDetails PayDetails { get; set; }
}
public class InsuranceDetails : IXmlSerializable
{
[XmlElement(ElementName = "Details1")]
public string Details1 { get; set; }
public void ReadXml(XmlReader reader)
{
reader.ReadStartElement("Insurance");
Details1 = reader.ReadElementString("Details1");
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer)
{
// do write suff
}
}
public class PaymentDetails
{
[XmlElement(ElementName = "Details1")]
public string Details1 { get; set; }
}
Given this example, using XmlSerializer to deserialize my string to QuoteObject, PaymentDetails is not successfully deserialized, returning a null object, because it seems it is expected to have IXmlDeserializable. It only works if PaymentDetails is parsed in first place. Is this some expected behavior from XmlSerializer?
using (TextReader read = new StringReader(xml))
{
var serializer = new XmlSerializer(typeof(Quote));
return (Quote)serializer.Deserialize(read);
}
Well these are the ReadXml and WriteXml I modified:
public void ReadXml(XmlReader reader)
{
reader.MoveToContent();
var empty=reader.IsEmptyElement;
reader.ReadStartElement();
if(!empty){
Details1=reader.ReadElementString("Details1");
reader.ReadEndElement();
}
}
public void WriteXml(XmlWriter writer)
{
var str=string.IsNullOrWhiteSpace(Details1)?"":Details1;
writer.WriteElementString("Details1",str);
}
Following are serialize and deserialize functions:
public static string Serialize<T>(T t)
{
var xmlser=new XmlSerializer(typeof(T));
XmlWriterSettings settings = new XmlWriterSettings();
using(StringWriter textWriter = new StringWriter()) {
using(XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings)) {
xmlser.Serialize(xmlWriter, t);
}
return textWriter.ToString();
}
}
public static T Deserialize<T>(string xml)
{
if(string.IsNullOrEmpty(xml)) {
return default(T);
}
XmlSerializer serializer = new XmlSerializer(typeof(T));
XmlReaderSettings settings = new XmlReaderSettings();
using(StringReader textReader = new StringReader(xml)) {
using(XmlReader xmlReader = XmlReader.Create(textReader, settings)) {
return (T) serializer.Deserialize(xmlReader);
}
}
}
Serialization Test:
var q=new Quote();
q.PayDetails = new PaymentDetails{Details1="Payment Details 1"};
q.InsDetails=new InsuranceDetails{Details1="Insurance Details 1"};
str = Serialize<Quote>(q);
Which gives (str):
<?xml version="1.0" encoding="utf-16"?>
<Quote xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Payment>
<Details1>Payment Details 1</Details1>
</Payment>
<Insurance>
<Details1>Insurance Details 1</Details1>
</Insurance>
</Quote>
Deserialization Test:
var dq=Deserialize<Quote>(str);
Console.WriteLine(dq.PaymentDetails.Detail1);//gives "Payment Details 1"
Console.WriteLine(dq.InsuranceDetails.Detail1);//gives "Insurance Details 1"
PS:- The Serialize code was copied from another SO answer verbatim. I learned how to serialize to string using StringWriter.
First of all you don't have to implement IXmlSerializable in any of the classes. Second of all, you don't provide the content of the xml variable. It may contain a mistype/bug, if you created it manually.
I used the following code, to test your classes:
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace XmlDeSerialize
{
[XmlRoot("Quote")]
public class Quote
{
[XmlElement("Insurance")]
public InsuranceDetails InsDetails { get; set; }
[XmlElement("Payment")]
public PaymentDetails PayDetails { get; set; }
}
public class InsuranceDetails
{
[XmlElement(ElementName = "Details1")]
public string Details1 { get; set; }
}
public class PaymentDetails
{
[XmlElement(ElementName = "Details1")]
public string Details1 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var qin = new Quote
{
InsDetails = new InsuranceDetails { Details1 = "insurance details text" },
PayDetails = new PaymentDetails { Details1 = "payment details text" },
};
string xml;
using (var stream = new MemoryStream())
{
var serializer = new XmlSerializer(typeof(Quote));
serializer.Serialize(stream, qin);
stream.Position = 0;
using (var sr = new StreamReader(stream))
{
xml = sr.ReadToEnd();
}
}
Quote qout;
using (TextReader read = new StringReader(xml))
{
var deserializer = new XmlSerializer(typeof(Quote));
var obj = deserializer.Deserialize(read);
qout = (Quote)obj;
}
Console.WriteLine("InsDetails.Details1='{0}'", qout.InsDetails.Details1);
Console.WriteLine("PayDetails.Details1='{0}'", qout.PayDetails.Details1);
}
}
}
The value of xml after serialization:
<?xml version="1.0"?>
<Quote xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Insurance>
<Details1>insurance details text</Details1>
</Insurance>
<Payment>
<Details1>payment details text</Details1>
</Payment>
</Quote>
The console output I received:
InsDetails.Details1='insurance details text'
PayDetails.Details1='payment details text'
Try the code yourself and see if it works for you. Clearly to me you don't provide valid XML content for deserialization, or other part of your code you did not provide in your question is to blame.

Saving and Loading a List

I'm working on a project where I have this list
List<Vara> minaVaror = new List<Vara>();
It's created from this class:
class Vara
{
public double streckKod { get; set; }
public string artNamn { get; set; }
}
And this is how I add items to the list:
minaVaror.Add(new Vara() {streckKod = inputBox1, artNamn = textBox2.Text });
Alright so this list is going to be added items to every now and then so I need to be able to save and load the content/items of the list so you won't lose the data when the program is closed and reopened.
We did something like this in class last year where we saved the data over to an XML file with XmlSerializer however that was only for 1 int, I'm not really sure how to do it for a whole list.
By definition, XmlSerializer cannot deserialize a List<T> or an ArrayList. From MSDN.
The XmlSerializer cannot deserialize the following: arrays of ArrayList and arrays of List<T>.
So you can serialize a List<T>, but you cannot deserialize List<T>.
So you can use this code to serialize in XML and deserialize an XML file.
namespace DataContractSerializerExample
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;
// You must apply a DataContractAttribute or SerializableAttribute
// to a class to have it serialized by the DataContractSerializer.
[DataContract(Name = "Vara", Namespace = "http://www.contoso.com")]
public class Vara
{
[DataMember()]
public double streckKod { get; set; }
[DataMember]
public string artNamn { get; set; }
}
public sealed class Test
{
private Test() { }
public static void Main()
{
List<Vara> minaVaror = new List<Vara>() { new Vara() { streckKod = 5.0, artNamn = "test1" }, new Vara() { streckKod = 5.0, artNamn = "test2" }, new Vara() { streckKod = 5.0, artNamn = "test3" } };
string fileName = "test.xml";
Serialize<List<Vara>>(fileName, minaVaror);
List<Vara> listDes = Deserialize<List<Vara>>(fileName);
}
public static void Serialize<T>(string fileName, T obj)
{
FileStream writer = new FileStream(fileName, FileMode.Create);
DataContractSerializer ser =
new DataContractSerializer(typeof(T));
ser.WriteObject(writer, obj);
writer.Close();
}
public static T Deserialize<T>(string fileName)
{
FileStream fs = new FileStream(fileName,
FileMode.Open);
XmlDictionaryReader reader =
XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas());
DataContractSerializer ser = new DataContractSerializer(typeof(T));
T des =
(T)ser.ReadObject(reader, true);
reader.Close();
fs.Close();
return des;
}
}
}
Note: You should add a reference to the C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.0\System.Runtime.Serialization.dll.

Categories