My data class that will be serialized into XML look like this:
[XmlType(TypeName = "SPCFileInfo")]
[Serializable]
public class SPCFileInfoProtocol
{
[XmlElement("CompanyCode")]
public string CompanyCode { get; set; }
[XmlElement("FileName")]
public string FileName { get; set; }
[XmlElement("FileVer")]
public int FileVer { get; set; }
[XmlElement("FileSize")]
public long FileSize { get; set; }
[XmlElement("CreatedOn")]
public DateTime CreatedOn { get; set; }
[XmlElement("LastUpdatedOn")]
public DateTime LastUpdatedOn { get; set; }
[XmlElement("FileBytes")]
public byte[] FileBytes { get; set; }
}
And here's my serialization utiltiy class
public static class XmlSerializer
{
public static string SerializeToString<T>(T item)
{
if (item == null)
{
return null;
}
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = new UnicodeEncoding(false, false); // no BOM in a .NET string
settings.Indent = false;
settings.OmitXmlDeclaration = false;
using (StringWriter textWriter = new StringWriter())
{
using (XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings))
{
serializer.Serialize(xmlWriter, item);
}
return textWriter.ToString();
}
}
public static T DeserializeFromString<T>(string xmlString)
{
T item = default(T);
try
{
using (StringReader stringReader = new StringReader(xmlString))
{
System.Xml.Serialization.XmlSerializer xmlSerializer =
new System.Xml.Serialization.XmlSerializer(typeof(T));
item = (T)xmlSerializer.Deserialize(stringReader);
}
}
catch (Exception ex)
{
Trace.WriteLine(ex.ToString());
}
return item;
}
}
Serialization into XML works fine, but when I attempt to deserialize, I get the following exception:
XMLException: There is an error in XML document.
hexadecimal value 0x00, is an invalid character.
Upon investigation, I found out that certain character codes are not valid for XML document. Removing invalid characters is not an option since they constitute the bytes for a file.
My question is how do you serialize/deserialize a data class like above into XML without stripping invalid bytes? If this is not possible, what are some viable alternatives?
Edit: Upon request, here's the full stacktrace of the error
System.InvalidOperationException: There is an error in XML document
(1, 21933). ---> System.Xml.XmlException: '.', hexadecimal value 0x00,
is an invalid character. Line 1, position 21933. at
System.Xml.XmlTextReaderImpl.Throw(Exception e) at
System.Xml.XmlTextReaderImpl.Throw(String res, String[] args) at
System.Xml.XmlTextReaderImpl.ParseText(Int32& startPos, Int32& endPos,
Int32& outOrChars) at System.Xml.XmlTextReaderImpl.ParseText()
at System.Xml.XmlTextReaderImpl.ParseElementContent() at
System.Xml.XmlTextReaderImpl.Read() at
System.Xml.XmlTextReader.Read() at
System.Xml.XmlReader.ReadElementString() at
Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderSPCCommandProtocol.Read2_SPCCommandProtocol(Boolean
isNullable, Boolean checkType) at
Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderSPCCommandProtocol.Read3_SPCCommand()
--- End of inner exception stack trace --- at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader
xmlReader, String encodingStyle, XmlDeserializationEvents events)
at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader
xmlReader) at
NextSPCFileUpdater.Utilities.XmlSerializer.DeserializeFromString[T](String
xmlString) in C:\Source
Codes\SPC\nextspc-fileupdater\NextSPCFileUpdater\Utilities\XmlSerializer.cs:line
48
And here's the new version of deserialization
public static T DeserializeFromString<T>(string xmlString)
{
T item = default(T);
try
{
using (StringReader stringReader = new StringReader(xmlString))
using (XmlTextReader xmlTextReader = new XmlTextReader(stringReader) { Normalization = false })
{
System.Xml.Serialization.XmlSerializer xmlSerializer =
new System.Xml.Serialization.XmlSerializer(typeof(T));
item = (T)xmlSerializer.Deserialize(xmlTextReader);
}
}
catch (Exception ex)
{
Trace.WriteLine(ex.ToString());
}
return item;
}
As you've noticed, there are lots of characters that may not be present in an XML document. These can be included in your data, however, using the proper escape sequence.
The default settings of the XmlTextReader cause it to mishandle this -- I think it interprets the escape sequences prematurely, but I'm not precisely certain. If I recall correctly, the XmlSerializer will create an XmlTextReader to wrap the TextReader you pass it. To override that, you need to create one yourself, setting its Normalization property of the XmlTextReader to false.
Regardless of whether my recollection of the causes of the problem is correct, however, setting Normalization to false will solve your problem:
var xmlReader = new XmlTextReader(textReader) { Normalization = false };
Or rather, in your case:
using (StringReader stringReader = new StringReader(xmlString))
using (XmlTextReader xmlTextReader = new XmlTextReader(stringReader) { Normalization = false })
{
System.Xml.Serialization.XmlSerializer xmlSerializer =
new System.Xml.Serialization.XmlSerializer(typeof(T));
item = (T)xmlSerializer.Deserialize(xmlTextReader);
}
As an aside, most will find your code far more readable if you use some using directives:
using System.Xml;
using System.Xml.Serialization;
using (StringReader stringReader = new StringReader(xmlString))
using (XmlTextReader xmlTextReader = new XmlTextReader(stringReader) { Normalization = false })
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
item = (T)xmlSerializer.Deserialize(xmlTextReader);
}
Still more will find it more readable if you use var (though I have at least one colleague who disagrees):
using System.Xml;
using System.Xml.Serialization;
using (var stringReader = new StringReader(xmlString))
using (var xmlTextReader = new XmlTextReader(stringReader) { Normalization = false })
{
var xmlSerializer = new XmlSerializer(typeof(T));
item = (T)xmlSerializer.Deserialize(xmlTextReader);
}
Related
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");
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.
I'm running into an issue where my JSON serializer is failing randomly due to the character < showing up from time to time. I can't nail down where this is coming from and I want to - on exception - reserialize using a different method so I can see a full representation of the offending object. Is there any way to do this?
My current code:
// data is of type 'object'
serialized = JsonConvert.SerializeObject(data, new JsonSerializerSettings() {
Error = delegate(object sender, ErrorEventArgs args) {
// reserialize here and output object so I know what the heck is going on
}
})
There is no foolproof way to serialize any and every possible c# object.
Instead, you have a few ways to attack your problem:
Turn on Json.NET tracing. See Debugging with Serialization Tracing. This should tell you where in your object graph the problem is occurring.
Rather than serializing with JsonConvert.SerializeObject(), if you serialize with JsonSerializer.Serialize() and write to a string using a JsonTextWriter wrapping a StringWriter, you can flush the writer and log the partial serialization. That may give some idea where the problem arises.
You can try serializing using various other serializers, and if any work, log the result.
If one of your object properties is throwing an exception, you might try to force serialization of fields instead. See JSON.Net: Force serialization of all private fields and all fields in sub-classes.
For instance, putting #1, #2 and #3 together gives the following method:
public static class JsonSerializerExtensions
{
public static string SerializeObject(object obj, JsonSerializerSettings settings = null)
{
settings = settings ?? new JsonSerializerSettings();
var sb = new StringBuilder();
using (var writer = new StringWriter(sb))
using (var jsonWriter = new JsonTextWriter(writer))
{
var oldError = settings.Error;
var oldTraceWriter = settings.TraceWriter;
var oldFormatting = settings.Formatting;
try
{
settings.Formatting = Newtonsoft.Json.Formatting.Indented;
if (settings.TraceWriter == null)
settings.TraceWriter = new MemoryTraceWriter();
settings.Error = oldError + delegate(object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args)
{
jsonWriter.Flush();
var logSb = new StringBuilder();
logSb.AppendLine("Serialization error: ");
logSb.Append("Path: ").Append(args.ErrorContext.Path).AppendLine();
logSb.Append("Member: ").Append(args.ErrorContext.Member).AppendLine();
logSb.Append("OriginalObject: ").Append(args.ErrorContext.OriginalObject).AppendLine();
logSb.AppendLine("Error: ").Append(args.ErrorContext.Error).AppendLine();
logSb.AppendLine("Partial serialization results: ").Append(sb).AppendLine();
logSb.AppendLine("TraceWriter contents: ").Append(settings.TraceWriter).AppendLine();
logSb.AppendLine("JavaScriptSerializer serialization: ");
try
{
logSb.AppendLine(new JavaScriptSerializer().Serialize(obj));
}
catch (Exception ex)
{
logSb.AppendLine("Failed, error: ").AppendLine(ex.ToString());
}
logSb.AppendLine("XmlSerializer serialization: ");
try
{
logSb.AppendLine(obj.GetXml());
}
catch (Exception ex)
{
logSb.AppendLine("Failed, error: ").AppendLine(ex.ToString());
}
logSb.AppendLine("BinaryFormatter serialization: ");
try
{
logSb.AppendLine(BinaryFormatterExtensions.ToBase64String(obj));
}
catch (Exception ex)
{
logSb.AppendLine("Failed, error: ").AppendLine(ex.ToString());
}
Debug.WriteLine(logSb);
};
var serializer = JsonSerializer.CreateDefault(settings);
serializer.Serialize(jsonWriter, obj);
}
finally
{
settings.Error = oldError;
settings.TraceWriter = oldTraceWriter;
settings.Formatting = oldFormatting;
}
}
return sb.ToString();
}
}
public static class XmlSerializerExtensions
{
public static T LoadFromXML<T>(this string xmlString)
{
using (StringReader reader = new StringReader(xmlString))
{
return (T)new XmlSerializer(typeof(T)).Deserialize(reader);
}
}
public static string GetXml<T>(this T obj)
{
using (var textWriter = new StringWriter())
{
var settings = new XmlWriterSettings() { Indent = true, IndentChars = " " };
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
new XmlSerializer(obj.GetType()).Serialize(xmlWriter, obj);
return textWriter.ToString();
}
}
}
public static class BinaryFormatterExtensions
{
public static string ToBase64String<T>(T obj)
{
using (var stream = new MemoryStream())
{
new BinaryFormatter().Serialize(stream, obj);
return Convert.ToBase64String(stream.GetBuffer(), 0, checked((int)stream.Length)); // Throw an exception on overflow.
}
}
public static T FromBase64String<T>(string data)
{
return FromBase64String<T>(data, null);
}
public static T FromBase64String<T>(string data, BinaryFormatter formatter)
{
using (var stream = new MemoryStream(Convert.FromBase64String(data)))
{
formatter = (formatter ?? new BinaryFormatter());
var obj = formatter.Deserialize(stream);
if (obj is T)
return (T)obj;
return default(T);
}
}
}
You would likely replace the final Debug.WriteLine() with an appropriate logging method, then replace JsonConvert.SerializeObject(data) with JsonSerializerExtensions.SerializeObject(data) in your applications code.
I have got a class named WebserviceType I got from the tool xsd.exe from an XSD file.
Now I want to deserialize an instance of an WebServiceType object to a string.
How can I do this?
The MethodCheckType object has as params a WebServiceType array.
My first try was like I serialized it: with a XmlSerializer and a StringWriter (while serializing I used a StringReader).
This is the method in which I serialize the WebServiceType object:
XmlSerializer serializer = new XmlSerializer(typeof(MethodCheckType));
MethodCheckType output = null;
StringReader reader = null;
// catch global exception, logg it and throw it
try
{
reader = new StringReader(path);
output = (MethodCheckType)serializer.Deserialize(reader);
}
catch (Exception)
{
throw;
}
finally
{
reader.Dispose();
}
return output.WebService;
Edit:
Maybe I could say it in different words: I have got an instance of this MethodCheckType object an on the other hand I have got the XML document from which I serialized this object. Now I want to convert this instance into a XML document in form of a string. After this I have to proof if both strings (of XML documents) are the same. This I have to do, because I make unit tests of the first method in which I read an XML document into a StringReader and serialize it into a MethodCheckType object.
Here are conversion method for both ways.
this = instance of your class
public string ToXML()
{
using(var stringwriter = new System.IO.StringWriter())
{
var serializer = new XmlSerializer(this.GetType());
serializer.Serialize(stringwriter, this);
return stringwriter.ToString();
}
}
public static YourClass LoadFromXMLString(string xmlText)
{
using(var stringReader = new System.IO.StringReader(xmlText))
{
var serializer = new XmlSerializer(typeof(YourClass ));
return serializer.Deserialize(stringReader) as YourClass ;
}
}
I realize this is a very old post, but after looking at L.B's response I thought about how I could improve upon the accepted answer and make it generic for my own application. Here's what I came up with:
public static string Serialize<T>(T dataToSerialize)
{
try
{
var stringwriter = new System.IO.StringWriter();
var serializer = new XmlSerializer(typeof(T));
serializer.Serialize(stringwriter, dataToSerialize);
return stringwriter.ToString();
}
catch
{
throw;
}
}
public static T Deserialize<T>(string xmlText)
{
try
{
var stringReader = new System.IO.StringReader(xmlText);
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stringReader);
}
catch
{
throw;
}
}
These methods can now be placed in a static helper class, which means no code duplication to every class that needs to be serialized.
public static string Serialize(object dataToSerialize)
{
if(dataToSerialize==null) return null;
using (StringWriter stringwriter = new System.IO.StringWriter())
{
var serializer = new XmlSerializer(dataToSerialize.GetType());
serializer.Serialize(stringwriter, dataToSerialize);
return stringwriter.ToString();
}
}
public static T Deserialize<T>(string xmlText)
{
if(String.IsNullOrWhiteSpace(xmlText)) return default(T);
using (StringReader stringReader = new System.IO.StringReader(xmlText))
{
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stringReader);
}
}
public static class XMLHelper
{
/// <summary>
/// Usage: var xmlString = XMLHelper.Serialize<MyObject>(value);
/// </summary>
/// <typeparam name="T">Kiểu dữ liệu</typeparam>
/// <param name="value">giá trị</param>
/// <param name="omitXmlDeclaration">bỏ qua declare</param>
/// <param name="removeEncodingDeclaration">xóa encode declare</param>
/// <returns>xml string</returns>
public static string Serialize<T>(T value, bool omitXmlDeclaration = false, bool omitEncodingDeclaration = true)
{
if (value == null)
{
return string.Empty;
}
try
{
var xmlWriterSettings = new XmlWriterSettings
{
Indent = true,
OmitXmlDeclaration = omitXmlDeclaration, //true: remove <?xml version="1.0" encoding="utf-8"?>
Encoding = Encoding.UTF8,
NewLineChars = "", // remove \r\n
};
var xmlserializer = new XmlSerializer(typeof(T));
using (var memoryStream = new MemoryStream())
{
using (var xmlWriter = XmlWriter.Create(memoryStream, xmlWriterSettings))
{
xmlserializer.Serialize(xmlWriter, value);
//return stringWriter.ToString();
}
memoryStream.Position = 0;
using (var sr = new StreamReader(memoryStream))
{
var pureResult = sr.ReadToEnd();
var resultAfterOmitEncoding = ReplaceFirst(pureResult, " encoding=\"utf-8\"", "");
if (omitEncodingDeclaration)
return resultAfterOmitEncoding;
return pureResult;
}
}
}
catch (Exception ex)
{
throw new Exception("XMLSerialize error: ", ex);
}
}
private static string ReplaceFirst(string text, string search, string replace)
{
int pos = text.IndexOf(search);
if (pos < 0)
{
return text;
}
return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
}
}
This is my solution, for any list object you can use this code for convert to xml layout. KeyFather is your principal tag and KeySon is where start your Forech.
public string BuildXml<T>(ICollection<T> anyObject, string keyFather, string keySon)
{
var settings = new XmlWriterSettings
{
Indent = true
};
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
StringBuilder builder = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
writer.WriteStartDocument();
writer.WriteStartElement(keyFather);
foreach (var objeto in anyObject)
{
writer.WriteStartElement(keySon);
foreach (PropertyDescriptor item in props)
{
writer.WriteStartElement(item.DisplayName);
writer.WriteString(props[item.DisplayName].GetValue(objeto).ToString());
writer.WriteEndElement();
}
writer.WriteEndElement();
}
writer.WriteFullEndElement();
writer.WriteEndDocument();
writer.Flush();
return builder.ToString();
}
}
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: ");
}
}