Read xml element while deserializing c# - c#

I have an xml document, where i serialize data dinamically, appending new data if i have a new request. The object properties i serialize are like this
[XmlRoot("LogRecords")]
public class LogRecord
{
public string Message { get; set; }
public DateTime SendTime { get; set; }
public string Sender { get; set; }
public string Recipient { get; set; }
}
Serializing is done in this way :
var stringwriter = new StringWriter();
var serializer = new XmlSerializer(object.GetType());
serializer.Serialize(stringwriter, object);
var smsxmlStr = stringwriter.ToString();
var smsRecordDoc = new XmlDocument();
smsRecordDoc.LoadXml(smsxmlStr);
var smsElement = smsRecordDoc.DocumentElement;
var smsLogFile = new XmlDocument();
smsLogFile.Load("LogRecords.xml");
var serialize = smsLogFile.CreateElement("LogRecord");
serialize.InnerXml = smsElement.InnerXml;
smsLogFile.DocumentElement.AppendChild(serialize);
smsLogFile.Save("LogRecords.xml");
While serializing i use LogFile.CreateElement("LogRecord") and my xml file looks like this :
<LogRecords>
<LogRecord>
<Message>Some messagge</Message>
<SendTime>2017-12-13T22:04:40.1109661+01:00</SendTime>
<Sender>Sender</Sender>
<Recipient>Name</Recipient>
</LogRecord>
<LogRecord>
<Message>Some message too</Message>
<SendTime>2017-12-13T22:05:08.5720173+01:00</SendTime>
<Sender>sender</Sender>
<Recipient>name</Recipient>
</LogRecord>
</LogRecords>
When i try to deserialize like this
XmlSerializer deserializer = new XmlSerializer(typeof(LogRecord));
TextReader reader = new StreamReader("LogRecords.xml");
object obj = deserializer.Deserialize(reader);
LogRecord records = (LogRecord)obj;
reader.Close();
I get null value for each property Message, Sender Recipient and a random value for SendTime, and i know it's because it doesn't recognise the XmlElement LogRecord i added while serializing..
Is there any way to read this xml element so i can take the right property values?
Ps. Sorry if i have messed up the variables, i tried to simplify the code when i added it here and i may have mixed some variables..
Thank you in advance.

You could try to generate POCO classes from XML in Visual Studio as it's described here.
You could serialize/deserialize those POCOs using with simple util methods like:
public static T DeserializeXML<T>(string content)
{
if (content == null)
return default(T);
XmlSerializer xs = new XmlSerializer(typeof(T));
byte[] byteArray = Encoding.ASCII.GetBytes(content);
var contentStream = new MemoryStream(byteArray);
var xml = xs.Deserialize(contentStream);
return (T)xml;
}
public static string SerializeAsXML(object item)
{
if (item == null)
return null;
XmlSerializer xs = new XmlSerializer(item.GetType());
using (var sw = new StringWriter())
{
using (XmlWriter writer = XmlWriter.Create(sw, new XmlWriterSettings { Indent = true }))
{
xs.Serialize(writer, item);
return sw.ToString();
}
}
}
LogRecords probably should be a collection (e.g. an array in this POCO):
/// <remarks/>
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class Log
{
/// <remarks/>
[System.Xml.Serialization.XmlArrayAttribute("LogRecords")]
[System.Xml.Serialization.XmlArrayItemAttribute("LogRecord", IsNullable = false)]
public LogRecord[] LogRecords { get; set; }
}
for the next XML format:
<Log>
<LogRecords>
<LogRecord>
<Message>Some messagge</Message>
<SendTime>2017-12-13T22:04:40.1109661+01:00</SendTime>
<Sender>Sender</Sender>
<Recipient>Name</Recipient>
</LogRecord>
<LogRecord>
<Message>Some message too</Message>
<SendTime>2017-12-13T22:05:08.5720173+01:00</SendTime>
<Sender>sender</Sender>
<Recipient>name</Recipient>
</LogRecord>
</LogRecords>
</Log>

You manually add the root element in the xml. Therefore, you must also manually skip it when reading.
XmlSerializer deserializer = new XmlSerializer(typeof(LogRecord));
using (var xmlReader = XmlReader.Create("LogRecords.xml"))
{
// Skip root element
xmlReader.ReadToFollowing("LogRecord");
LogRecord record = (LogRecord)deserializer.Deserialize(xmlReader);
}
Remove the [XmlRoot("LogRecords")] attribute to make it work.
Of course, you will always get the first element in the xml.
As already suggested in the comments, use the list.
List<LogRecord> logRecords = new List<LogRecord>();
var logRecord = new LogRecord { ... };
// Store each new logRecord to list
logRecords.Add(logRecord);
var serializer = new XmlSerializer(typeof(List<LogRecord>));
// Serialization is done with just a couple lines of code.
using (var fileStream = new FileStream("LogRecords.xml", FileMode.Create))
{
serializer.Serialize(fileStream, logRecords);
}
// As well as deserialization
using (var fileStream = new FileStream("LogRecords.xml", FileMode.Open))
{
logRecords = (List<LogRecord>)serializer.Deserialize(fileStream);
}
Thus become unnecessary manipulation using XmlDocument and fuss with manually adding-skipping the root node.

Related

Append to the last node of xml file c#

Each time i get a request from a user, i have to serialize and append it , to an existing xml file like this :
<LogRecords>
<LogRecord>
<Message>Some messagge</Message>
<SendTime>2017-12-13T22:04:40.1109661+01:00</SendTime>
<Sender>Sender</Sender>
<Recipient>Name</Recipient>
</LogRecord>
<LogRecord>
<Message>Some message too</Message>
<SendTime>2017-12-13T22:05:08.5720173+01:00</SendTime>
<Sender>sender</Sender>
<Recipient>name</Recipient>
</LogRecord>
</LogRecords>
Currently Serializing data in this way (which works fine):
var stringwriter = new StringWriter();
var serializer = new XmlSerializer(object.GetType());
serializer.Serialize(stringwriter, object);
var smsxmlStr = stringwriter.ToString();
var smsRecordDoc = new XmlDocument();
smsRecordDoc.LoadXml(smsxmlStr);
var smsElement = smsRecordDoc.DocumentElement;
var smsLogFile = new XmlDocument();
smsLogFile.Load("LogRecords.xml");
var serialize = smsLogFile.CreateElement("LogRecord");
serialize.InnerXml = smsElement.InnerXml;
smsLogFile.DocumentElement.AppendChild(serialize);
smsLogFile.Save("LogRecords.xml");
And the properties class
[XmlRoot("LogRecords")]
public class LogRecord
{
public string Message { get; set; }
public DateTime SendTime { get; set; }
public string Sender { get; set; }
public string Recipient { get; set; }
}
But what i want to do is to load the file, navigate to the last element/node of it and append a new List<LogRecord> and save, so i can easily deserialize later.
I have tried various ways using XPath Select Methods like SelectSingleNode and SelectNodes but since i am junior with c# i haven't manage to make them work properly. Does anyone have any idea on how to serialize and append properly?
Thank you
Your approach (and most of the answers given to date) rely on having all of the log file in memory in order to append more records to it. As the log file grows, this could cause issues (such as OutOfMemoryException errors) down the road. Your best bet is to use an approach that streams the data from the original file into a new file. While there might be a few bugs in my untested code. The approach would look something like the following:
// What you are serializing
var obj = default(object);
using (var reader = XmlReader.Create("LogRecords.xml"))
using (var writer = XmlWriter.Create("LogRecords2.xml"))
{
// Start the log file
writer.WriteStartElement("LogRecords");
while (reader.Read())
{
// When you see a record in the original file, copy it to the output
if (reader.NodeType == XmlNodeType.Element && reader.LocalName == "LogRecord")
{
writer.WriteNode(reader.ReadSubtree(), false);
}
}
// Add your additional record(s) to the output
var serializer = new XmlSerializer(obj.GetType());
serializer.Serialize(writer, obj);
// Close the tag
writer.WriteEndElement();
}
// Replace the original file with the new file.
System.IO.File.Delete("LogRecords.xml");
System.IO.File.Move("LogRecords2.xml", "LogRecords.xml");
Another idea to consider, does the log file need to be a valid XML file (with the <LogRecords> tag at the start and finish? If you omit the root tag, you could simply append the new records at the bottom of the file (which should be very efficient). You can still read the XML in .Net by creating an XmlReader with the right ConformanceLevel. For example
var settings = new XmlReaderSettings()
{
ConformanceLevel = ConformanceLevel.Fragment
};
using (var reader = XmlReader.Create("LogRecords.xml", settings))
{
// Do something with the records here
}
Try using xml linq :
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
LogRecord record = doc.Descendants("LogRecord").Select(x => new LogRecord()
{
Message = (string)x.Element("Message"),
SendTime = (DateTime)x.Element("SendTime"),
Sender = (string)x.Element("Sender"),
Recipient = (string)x.Element("Recipient")
}).OrderByDescending(x => x.SendTime).FirstOrDefault();
}
}
public class LogRecord
{
public string Message { get; set; }
public DateTime SendTime { get; set; }
public string Sender { get; set; }
public string Recipient { get; set; }
}
}
You can perform it by using XDocument like this;
XDocument doc = XDocument.Load("LogRecords.xml");
//Append Node
XElement logRecord = new XElement("LogRecord");
XElement message = new XElement("Message");
message.Value = "Message";
XElement sendTime = new XElement("SendTime");
sendTime.Value = "SendTime";
XElement sender = new XElement("Sender");
sender.Value = "Sender";
XElement recipient = new XElement("Recipient");
recipient.Value = "Recipient";
logRecord.Add(message);
logRecord.Add(sendTime);
logRecord.Add(sender);
logRecord.Add(recipient);
doc.Element("LogRecords").Add(logRecord);
//Append Node
doc.Save("LogRecords.xml");

Add dynamically xml tag to serialized object without modifying the object

I have an object serializable like this :
[XmlRoot("MyObject")]
[Serializable]
public class MyObject
{
public MyObject()
{
}
[XmlAttribute("name")]
public string Name { get; set; }
}
Right now I am able to export this object in an xml file with the following code :
public byte[] Export(MyObject myObject)
{
XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
byte[] value = null;
using (MemoryStream stream = new MemoryStream())
{
StreamWriter writer = new StreamWriter(stream);
serializer.Serialize(writer, myObject);
value = stream.ToArray();
}
return value;
}
For the moment, my exported xml is like this :
<MyObject Name="nameData">
I would like to export other data (a list of name related to myObject and obtained from another service) with this. But I cannot modify my MyObject class.
This is how I get the additionnal data :
public XElement GetExtraData(MyObject myObject)
{
List<string> datas = service.GetData(myObject);
var otherDatas = new XElement("otherDatas");
foreach (var data in datas)
{
var dataElement = new XElement("data");
var valueAttribute = new XAttribute("Value", data);
dataElement.Add(valueAttribute);
}
otherDatas.Add(dataElement);
}
I would like to append this otherDatas element to my previous element in order to obtain the following :
<MyObject Name="nameData">
<data Value="valueData">
<data Value="valueData">
<data Value="valueData">
But I don't see how, as this new method returns me a xElement created by me, and the natively .net serialization returns me an array of bytes.
Add the XElement returned from GetExtraData as Nodes to the original xml:
var doc = new XDocument(GetExtraData(new MyObject { Name = "someOtherName" }));
var byt = Export(new MyObject { Name = "nameData" });
using (var stream = new MemoryStream(byt))
{
var element = new XElement(XElement.Load(stream));
element.Add(doc.Nodes());
}
And save element

How to serialize to xml using Linq

How can i serialize Instance of College to XML using Linq?
class College
{
public string Name { get; set; }
public string Address { get; set; }
public List<Person> Persons { get; set; }
}
class Person
{
public string Gender { get; set; }
public string City { get; set; }
}
You can't serialize with LINQ. You can use XmlSerializer.
XmlSerializer serializer = new XmlSerializer(typeof(College));
// Create a FileStream to write with.
Stream writer = new FileStream(filename, FileMode.Create);
// Serialize the object, and close the TextWriter
serializer.Serialize(writer, i);
writer.Close();
Not sure why people are saying you can't serialize/deserialize with LINQ. Custom serialization is still serialization:
public static College Deserialize(XElement collegeXML)
{
return new College()
{
Name = (string)collegeXML.Element("Name"),
Address = (string)collegeXML.Element("Address"),
Persons = (from personXML in collegeXML.Element("Persons").Elements("Person")
select Person.Deserialize(personXML)).ToList()
}
}
public static XElement Serialize(College college)
{
return new XElement("College",
new XElement("Name", college.Name),
new XElement("Address", college.Address)
new XElement("Persons", (from p in college.Persons
select Person.Serialize(p)).ToList()));
);
Note, this probably isn't the greatest approach, but it's answering the question at least.
You can't use LINQ. Look at the below code as an example.
// This is the test class we want to
// serialize:
[Serializable()]
public class TestClass
{
private string someString;
public string SomeString
{
get { return someString; }
set { someString = value; }
}
private List<string> settings = new List<string>();
public List<string> Settings
{
get { return settings; }
set { settings = value; }
}
// These will be ignored
[NonSerialized()]
private int willBeIgnored1 = 1;
private int willBeIgnored2 = 1;
}
// Example code
// This example requires:
// using System.Xml.Serialization;
// using System.IO;
// Create a new instance of the test class
TestClass TestObj = new TestClass();
// Set some dummy values
TestObj.SomeString = "foo";
TestObj.Settings.Add("A");
TestObj.Settings.Add("B");
TestObj.Settings.Add("C");
#region Save the object
// Create a new XmlSerializer instance with the type of the test class
XmlSerializer SerializerObj = new XmlSerializer(typeof(TestClass));
// Create a new file stream to write the serialized object to a file
TextWriter WriteFileStream = new StreamWriter(#"C:\test.xml");
SerializerObj.Serialize(WriteFileStream, TestObj);
// Cleanup
WriteFileStream.Close();
#endregion
/*
The test.xml file will look like this:
<?xml version="1.0"?>
<TestClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SomeString>foo</SomeString>
<Settings>
<string>A</string>
<string>B</string>
<string>C</string>
</Settings>
</TestClass>
*/
#region Load the object
// Create a new file stream for reading the XML file
FileStream ReadFileStream = new FileStream(#"C:\test.xml", FileMode.Open, FileAccess.Read, FileShare.Read);
// Load the object saved above by using the Deserialize function
TestClass LoadedObj = (TestClass)SerializerObj.Deserialize(ReadFileStream);
// Cleanup
ReadFileStream.Close();
#endregion
// Test the new loaded object:
MessageBox.Show(LoadedObj.SomeString);
foreach (string Setting in LoadedObj.Settings)
MessageBox.Show(Setting);
you have to use the XML serialization
static public void SerializeToXML(College college)
{
XmlSerializer serializer = new XmlSerializer(typeof(college));
TextWriter textWriter = new StreamWriter(#"C:\college.xml");
serializer.Serialize(textWriter, college);
textWriter.Close();
}
You can use that if you needed XDocument object after serialization
DataClass dc = new DataClass();
XmlSerializer x = new XmlSerializer(typeof(DataClass));
MemoryStream ms = new MemoryStream();
x.Serialize(ms, dc);
ms.Seek(0, 0);
XDocument xDocument = XDocument.Load(ms); // Here it is!
I'm not sure if that is what you want, but to make an XML-Document out of this:
College coll = ...
XDocument doc = new XDocument(
new XElement("College",
new XElement("Name", coll.Name),
new XElement("Address", coll.Address),
new XElement("Persons", coll.Persons.Select(p =>
new XElement("Person",
new XElement("Gender", p.Gender),
new XElement("City", p.City)
)
)
)
);

Xml Serialization without XML Declaration [duplicate]

How do I serialize an XML-serializable object to an XML fragment (no XML declaration nor namespace references in the root element)?
Here is a hack-ish way to do it without having to load the entire output string into an XmlDocument:
using System;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
public class Example
{
public String Name { get; set; }
static void Main()
{
Example example = new Example { Name = "Foo" };
XmlSerializer serializer = new XmlSerializer(typeof(Example));
XmlSerializerNamespaces emptyNamespace = new XmlSerializerNamespaces();
emptyNamespace.Add(String.Empty, String.Empty);
StringBuilder output = new StringBuilder();
XmlWriter writer = XmlWriter.Create(output,
new XmlWriterSettings { OmitXmlDeclaration = true });
serializer.Serialize(writer, example, emptyNamespace);
Console.WriteLine(output.ToString());
}
}
You should be able to just serialize like you usually do, and then use the Root property from the resulting document.
You may need to clear the attributes of the element first.
By the way this is awesome.
I implemented this code to make it easy to work with xml fragments as classes quickly and then you can just replace the node when finished. This makes the transition between code and xml ultra-easy.
First create some extension methods.
public static class SerializableFragmentExtensions
{
public static XElement ToElement(this ISerializableFragment iSerializableFragment)
{
var serializer = new XmlSerializer(iSerializableFragment.GetType());
var emptyNamespace = new XmlSerializerNamespaces();
emptyNamespace.Add(String.Empty, String.Empty);
var output = new StringBuilder();
var writer = XmlWriter.Create(output,
new XmlWriterSettings { OmitXmlDeclaration = true });
serializer.Serialize(writer, iSerializableFragment, emptyNamespace);
return XElement.Parse(output.ToString(), LoadOptions.None);
}
public static T ToObject<T>(this XElement xElement)
{
var serializer = new XmlSerializer(typeof (T));
var reader = xElement.CreateReader();
var obj = (T) serializer.Deserialize(reader);
return obj;
}
}
Next Implement the required interface (marker interface--I know you are not supposed to but I think this is the perfect reason to it.)
public interface ISerializableFragment
{
}
Now all you have to do is decorate any Serializable class, you want to convert to an XElement Fragment, with the interface.
[Serializable]
public class SomeSerializableClass : ISerializableFragment
{
[XmlAttribute]
public string SomeData { get; set; }
}
Finally test the code.
static void Main(string[] args)
{
var someSerializableClassObj = new SomeSerializableClass() {SomeData = "Testing"};
var element = someSerializableClass.ToElement();
var backToSomeSerializableClassObj = element.ToObject<SomeSerializableClass>();
}
Thanks again for this amazingly useful code.

Serialize an object to XElement and Deserialize it in memory

I want to serialize an object to XML, but I don't want to save it on the disk. I want to hold it in a XElement variable (for using with LINQ), and then Deserialize back to my object.
How can I do this?
You can use these two extension methods to serialize and deserialize between XElement and your objects.
public static XElement ToXElement<T>(this object obj)
{
using (var memoryStream = new MemoryStream())
{
using (TextWriter streamWriter = new StreamWriter(memoryStream))
{
var xmlSerializer = new XmlSerializer(typeof(T));
xmlSerializer.Serialize(streamWriter, obj);
return XElement.Parse(Encoding.ASCII.GetString(memoryStream.ToArray()));
}
}
}
public static T FromXElement<T>(this XElement xElement)
{
var xmlSerializer = new XmlSerializer(typeof(T));
return (T)xmlSerializer.Deserialize(xElement.CreateReader());
}
USAGE
XElement element = myClass.ToXElement<MyClass>();
var newMyClass = element.FromXElement<MyClass>();
You can use XMLSerialization
XML serialization is the process of converting an object's public
properties and fields to a serial format (in this case, XML) for
storage or transport. Deserialization re-creates the object in its
original state from the XML output. You can think of serialization as
a way of saving the state of an object into a stream or buffer. For
example, ASP.NET uses the XmlSerializer class to encode XML Web
service messages
and XDocument Represents an XML document to achieve this
using System;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
namespace ConsoleApplication5
{
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
XmlSerializer xs = new XmlSerializer(typeof(Person));
Person p = new Person();
p.Age = 35;
p.Name = "Arnold";
Console.WriteLine("\n Before serializing...\n");
Console.WriteLine(string.Format("Age = {0} Name = {1}", p.Age,p.Name));
XDocument d = new XDocument();
using (XmlWriter xw = d.CreateWriter())
xs.Serialize(xw, p);
// you can use LINQ on elm now
XElement elm = d.Root;
Console.WriteLine("\n From XElement...\n");
elm.Elements().All(e => { Console.WriteLine(string.Format("element name {0} , element value {1}", e.Name, e.Value)); return true; });
//deserialize back to object
Person pDeserialized = xs.Deserialize((d.CreateReader())) as Person;
Console.WriteLine("\n After deserializing...\n");
Console.WriteLine(string.Format("Age = {0} Name = {1}", p.Age, p.Name));
Console.ReadLine();
}
}
}
and here is output
(Late answer)
Serialize:
var doc = new XDocument();
var xmlSerializer = new XmlSerializer(typeof(MyClass));
using (var writer = doc.CreateWriter())
{
xmlSerializer.Serialize(writer, obj);
}
// now you can use `doc`(XDocument) or `doc.Root` (XElement)
Deserialize:
MyClass obj;
using(var reader = doc.CreateReader())
{
obj = (MyClass)xmlSerializer.Deserialize(reader);
}
ToXelement without Code Analysis issues, same answer as Abdul Munim but fixed the CA issues (except for CA1004, this cannot be resolved in this case and is by design)
public static XElement ToXElement<T>(this object value)
{
MemoryStream memoryStream = null;
try
{
memoryStream = new MemoryStream();
using (TextWriter streamWriter = new StreamWriter(memoryStream))
{
memoryStream = null;
var xmlSerializer = new XmlSerializer(typeof(T));
xmlSerializer.Serialize(streamWriter, value);
return XElement.Parse(Encoding.ASCII.GetString(memoryStream.ToArray()));
}
}
finally
{
if (memoryStream != null)
{
memoryStream.Dispose();
}
}
}
What about
public static byte[] BinarySerialize(Object obj)
{
byte[] serializedObject;
MemoryStream ms = new MemoryStream();
BinaryFormatter b = new BinaryFormatter();
try
{
b.Serialize(ms, obj);
ms.Seek(0, 0);
serializedObject = ms.ToArray();
ms.Close();
return serializedObject;
}
catch
{
throw new SerializationException("Failed to serialize. Reason: ");
}
}

Categories

Resources