Convert stream to object with generics [duplicate] - c#

I have heard that Json.NET is faster than DataContractJsonSerializer, and wanted to give it a try...
But I couldn't find any methods on JsonConvert that take a stream rather than a string.
For deserializing a file containing JSON on WinPhone, for example, I use the following code to read the file contents into a string, and then deserialize into JSON. It appears to be about 4 times slower in my (very ad-hoc) testing than using DataContractJsonSerializer to deserialize straight from the stream...
// DCJS
DataContractJsonSerializer dc = new DataContractJsonSerializer(typeof(Constants));
Constants constants = (Constants)dc.ReadObject(stream);
// JSON.NET
string json = new StreamReader(stream).ReadToEnd();
Constants constants = JsonConvert.DeserializeObject<Constants>(json);
Am I doing it wrong?

The current version of Json.net does not allow you to use the accepted answer code. A current alternative is:
public static object DeserializeFromStream(Stream stream)
{
var serializer = new JsonSerializer();
using (var sr = new StreamReader(stream))
using (var jsonTextReader = new JsonTextReader(sr))
{
return serializer.Deserialize(jsonTextReader);
}
}
Documentation: Deserialize JSON from a file stream

public static void Serialize(object value, Stream s)
{
using (StreamWriter writer = new StreamWriter(s))
using (JsonTextWriter jsonWriter = new JsonTextWriter(writer))
{
JsonSerializer ser = new JsonSerializer();
ser.Serialize(jsonWriter, value);
jsonWriter.Flush();
}
}
public static T Deserialize<T>(Stream s)
{
using (StreamReader reader = new StreamReader(s))
using (JsonTextReader jsonReader = new JsonTextReader(reader))
{
JsonSerializer ser = new JsonSerializer();
return ser.Deserialize<T>(jsonReader);
}
}

UPDATE: This no longer works in the current version, see below for correct answer (no need to vote down, this is correct on older versions).
Use the JsonTextReader class with a StreamReader or use the JsonSerializer overload that takes a StreamReader directly:
var serializer = new JsonSerializer();
serializer.Deserialize(streamReader);

I've written an extension class to help me deserializing from JSON sources (string, stream, file).
public static class JsonHelpers
{
public static T CreateFromJsonStream<T>(this Stream stream)
{
JsonSerializer serializer = new JsonSerializer();
T data;
using (StreamReader streamReader = new StreamReader(stream))
{
data = (T)serializer.Deserialize(streamReader, typeof(T));
}
return data;
}
public static T CreateFromJsonString<T>(this String json)
{
T data;
using (MemoryStream stream = new MemoryStream(System.Text.Encoding.Default.GetBytes(json)))
{
data = CreateFromJsonStream<T>(stream);
}
return data;
}
public static T CreateFromJsonFile<T>(this String fileName)
{
T data;
using (FileStream fileStream = new FileStream(fileName, FileMode.Open))
{
data = CreateFromJsonStream<T>(fileStream);
}
return data;
}
}
Deserializing is now as easy as writing:
MyType obj1 = aStream.CreateFromJsonStream<MyType>();
MyType obj2 = "{\"key\":\"value\"}".CreateFromJsonString<MyType>();
MyType obj3 = "data.json".CreateFromJsonFile<MyType>();
Hope it will help someone else.

I arrived at this question looking for a way to stream an open ended list of objects onto a System.IO.Stream and read them off the other end, without buffering the entire list before sending. (Specifically I'm streaming persisted objects from MongoDB over Web API.)
#Paul Tyng and #Rivers did an excellent job answering the original question, and I used their answers to build a proof of concept for my problem. I decided to post my test console app here in case anyone else is facing the same issue.
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace TestJsonStream {
class Program {
static void Main(string[] args) {
using(var writeStream = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.None)) {
string pipeHandle = writeStream.GetClientHandleAsString();
var writeTask = Task.Run(() => {
using(var sw = new StreamWriter(writeStream))
using(var writer = new JsonTextWriter(sw)) {
var ser = new JsonSerializer();
writer.WriteStartArray();
for(int i = 0; i < 25; i++) {
ser.Serialize(writer, new DataItem { Item = i });
writer.Flush();
Thread.Sleep(500);
}
writer.WriteEnd();
writer.Flush();
}
});
var readTask = Task.Run(() => {
var sw = new Stopwatch();
sw.Start();
using(var readStream = new AnonymousPipeClientStream(pipeHandle))
using(var sr = new StreamReader(readStream))
using(var reader = new JsonTextReader(sr)) {
var ser = new JsonSerializer();
if(!reader.Read() || reader.TokenType != JsonToken.StartArray) {
throw new Exception("Expected start of array");
}
while(reader.Read()) {
if(reader.TokenType == JsonToken.EndArray) break;
var item = ser.Deserialize<DataItem>(reader);
Console.WriteLine("[{0}] Received item: {1}", sw.Elapsed, item);
}
}
});
Task.WaitAll(writeTask, readTask);
writeStream.DisposeLocalCopyOfClientHandle();
}
}
class DataItem {
public int Item { get; set; }
public override string ToString() {
return string.Format("{{ Item = {0} }}", Item);
}
}
}
}
Note that you may receive an exception when the AnonymousPipeServerStream is disposed, I ignored this as it isn't relevant to the problem at hand.

another option that is handy when you are running out of memory is to periodically flush
/// <summary>serialize the value in the stream.</summary>
/// <typeparam name="T">the type to serialize</typeparam>
/// <param name="stream">The stream.</param>
/// <param name="value">The value.</param>
/// <param name="settings">The json settings to use.</param>
/// <param name="bufferSize"></param>
/// <param name="leaveOpen"></param>
public static void JsonSerialize<T>(this Stream stream,[DisallowNull] T value, [DisallowNull] JsonSerializerSettings settings, int bufferSize=1024, bool leaveOpen=false)
{
using (var writer = new StreamWriter(stream,encoding: System.Text.Encoding.UTF32,bufferSize,leaveOpen))
using (var jsonWriter = new JsonTextWriter(writer))
{
var ser = JsonSerializer.Create( settings );
ser.Serialize(jsonWriter, value);
jsonWriter.Flush();
}
}
/// <summary>serialize the value in the stream asynchronously.</summary>
/// <typeparam name="T"></typeparam>
/// <param name="stream">The stream.</param>
/// <param name="value">The value.</param>
/// <param name="settings">The settings.</param>
/// <param name="bufferSize">The buffer size, in bytes, set -1 to not flush till done</param>
/// <param name="leaveOpen"> true to leave the stream open </param>
/// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
public static Task JsonSerializeAsync<T>(this Stream stream,[DisallowNull] T value, [DisallowNull] JsonSerializerSettings settings, int bufferSize=1024, bool leaveOpen=false, CancellationToken cancellationToken=default)
{
using (var writer = new StreamWriter(stream,encoding: System.Text.Encoding.UTF32,bufferSize: bufferSize,leaveOpen: leaveOpen))
using (var jsonWriter = new JsonTextWriter(writer))
{
var ser = JsonSerializer.Create( settings );
ser.Serialize(jsonWriter, value);
return jsonWriter.Flush();
}
//jsonWriter.FlushAsnc with my version gives an error on the stream
return Task.CompletedTask;
}
You can test/ use it like so:
[TestMethod()]
public void WriteFileIntoJsonTest()
{
var file = new FileInfo(Path.GetTempFileName());
try
{
var list = new HashSet<Guid>();
for (int i = 0; i < 100; i++)
{
list.Add(Guid.NewGuid());
}
file.JsonSerialize(list);
var sr = file.IsValidJson<List<Guid>>(out var result);
Assert.IsTrue(sr);
Assert.AreEqual<int>(list.Count, result.Count);
foreach (var item in result)
{
Assert.IsFalse(list.Add(item), $"The GUID {item} should already exist in the hash set");
}
}
finally
{
file.Refresh();
file.Delete();
}
}
you'd need to create the extension methods, here is the whole set:
public static class JsonStreamReaderExt
{
static JsonSerializerSettings _settings ;
static JsonStreamReaderExt()
{
_settings = JsonConvert.DefaultSettings?.Invoke() ?? new JsonSerializerSettings();
_settings.ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor;
_settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
_settings.DateFormatHandling = DateFormatHandling.IsoDateFormat ;
}
/// <summary>
/// serialize the value in the stream.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="stream">The stream.</param>
/// <param name="value">The value.</param>
public static void JsonSerialize<T>(this Stream stream,[DisallowNull] T value)
{
stream.JsonSerialize(value,_settings);
}
/// <summary>
/// serialize the value in the file .
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="file">The file.</param>
/// <param name="value">The value.</param>
public static void JsonSerialize<T>(this FileInfo file,[DisallowNull] T value)
{
if (string.IsNullOrEmpty(file.DirectoryName)==true && Directory.Exists(file.DirectoryName) == false)
{
Directory.CreateDirectory(file.FullName);
}
using var s = file.OpenWrite();
s.JsonSerialize(value, _settings);
file.Refresh();
}
/// <summary>
/// serialize the value in the file .
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="file">The file.</param>
/// <param name="value">The value.</param>
/// <param name="settings">the json settings to use</param>
public static void JsonSerialize<T>(this FileInfo file, [DisallowNull] T value, [DisallowNull] JsonSerializerSettings settings)
{
if (string.IsNullOrEmpty(file.DirectoryName) == true && Directory.Exists(file.DirectoryName) == false)
{
Directory.CreateDirectory(file.FullName);
}
using var s = file.OpenWrite();
s.JsonSerialize(value, settings);
file.Refresh();
}
/// <summary>
/// serialize the value in the file .
/// </summary>
/// <remarks>File will be refreshed to contain the new meta data</remarks>
/// <typeparam name="T">the type to serialize</typeparam>
/// <param name="file">The file.</param>
/// <param name="value">The value.</param>
/// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
public static async Task JsonSerializeAsync<T>(this FileInfo file, [DisallowNull] T value, CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(file.DirectoryName) == true && Directory.Exists(file.DirectoryName) == false)
{
Directory.CreateDirectory(file.FullName);
}
using (var stream = file.OpenWrite())
{
await stream.JsonSerializeAsync(value, _settings,bufferSize:1024,leaveOpen:false, cancellationToken).ConfigureAwait(false);
}
file.Refresh();
}
/// <summary>
/// serialize the value in the file .
/// </summary>
/// <remarks>File will be refreshed to contain the new meta data</remarks>
/// <typeparam name="T">the type to serialize</typeparam>
/// <param name="file">The file to create or overwrite.</param>
/// <param name="value">The value.</param>
/// <param name="settings">the json settings to use</param>
/// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
public static async Task JsonSerializeAsync<T>(this FileInfo file, [DisallowNull] T value, [DisallowNull] JsonSerializerSettings settings, CancellationToken cancellationToken=default)
{
if (string.IsNullOrEmpty(file.DirectoryName) == true && Directory.Exists(file.DirectoryName) == false)
{
Directory.CreateDirectory(file.FullName);
}
using (var stream = file.OpenWrite())
{
await stream.JsonSerializeAsync(value, settings,bufferSize:1024,leaveOpen:false, cancellationToken).ConfigureAwait(false);
}
file.Refresh();
}
/// <summary>serialize the value in the stream.</summary>
/// <typeparam name="T">the type to serialize</typeparam>
/// <param name="stream">The stream.</param>
/// <param name="value">The value.</param>
/// <param name="settings">The json settings to use.</param>
/// <param name="bufferSize">The buffer size, in bytes, set -1 to not flush till done</param>
/// <param name="leaveOpen"> true to leave the stream open </param>
public static void JsonSerialize<T>(this Stream stream,[DisallowNull] T value, [DisallowNull] JsonSerializerSettings settings, int bufferSize=1024, bool leaveOpen=false)
{
using (var writer = new StreamWriter(stream,encoding: System.Text.Encoding.UTF32,bufferSize,leaveOpen))
using (var jsonWriter = new JsonTextWriter(writer))
{
var ser = JsonSerializer.Create( settings );
ser.Serialize(jsonWriter, value);
jsonWriter.Flush();
}
}
/// <summary>serialize the value in the stream asynchronously.</summary>
/// <typeparam name="T"></typeparam>
/// <param name="stream">The stream.</param>
/// <param name="value">The value.</param>
/// <param name="settings">The settings.</param>
/// <param name="bufferSize">The buffer size, in bytes, set -1 to not flush till done</param>
/// <param name="leaveOpen"> true to leave the stream open </param>
/// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
public static Task JsonSerializeAsync<T>(this Stream stream,[DisallowNull] T value, [DisallowNull] JsonSerializerSettings settings, int bufferSize=1024, bool leaveOpen=false, CancellationToken cancellationToken=default)
{
using (var writer = new StreamWriter(stream,encoding: System.Text.Encoding.UTF32,bufferSize: bufferSize,leaveOpen: leaveOpen))
using (var jsonWriter = new JsonTextWriter(writer))
{
var ser = JsonSerializer.Create( settings );
ser.Serialize(jsonWriter, value);
jsonWriter.Flush();
}
return Task.CompletedTask;
}
/// <summary>
/// Determines whether [is valid json] [the specified result].
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="stream">The stream.</param>
/// <param name="result">The result.</param>
/// <returns><c>true</c> if [is valid json] [the specified result]; otherwise, <c>false</c>.</returns>
public static bool IsValidJson<T>(this Stream stream, [NotNullWhen(true)] out T? result)
{
if (stream is null)
{
throw new ArgumentNullException(nameof(stream));
}
if (stream.Position != 0)
{
stream.Seek(0, SeekOrigin.Begin);
}
JsonSerializerSettings settings = (JsonConvert.DefaultSettings?.Invoke()) ?? new JsonSerializerSettings() { DateTimeZoneHandling = DateTimeZoneHandling.Utc, DateFormatHandling = DateFormatHandling.IsoDateFormat };
settings.ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor;
using (var reader = new StreamReader(stream))
using (var jsonReader = new JsonTextReader(reader))
{
var ser = JsonSerializer.Create(settings);
try
{
result = ser.Deserialize<T>(jsonReader);
}
catch { result = default; }
}
return result is not null;
}
/// <summary>
/// Determines whether [is valid json] [the specified settings].
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="stream">The stream.</param>
/// <param name="settings">The settings.</param>
/// <param name="result">The result.</param>
/// <returns><c>true</c> if [is valid json] [the specified settings]; otherwise, <c>false</c>.</returns>
public static bool IsValidJson<T>(this Stream stream, JsonSerializerSettings settings, [NotNullWhen(true)] out T? result)
{
if (stream is null)
{
throw new ArgumentNullException(nameof(stream));
}
if (settings is null)
{
throw new ArgumentNullException(nameof(settings));
}
if (stream.Position != 0)
{
stream.Seek(0, SeekOrigin.Begin);
}
using (var reader = new StreamReader(stream))
using (var jsonReader = new JsonTextReader(reader))
{
var ser = JsonSerializer.Create(settings);
try
{
result = ser.Deserialize<T>(jsonReader);
}
catch { result = default; }
}
return result is not null;
}
/// <summary>
/// Determines whether file contains valid json using the specified settings and reads it into the output.
/// </summary>
/// <typeparam name="T">Type to convert into</typeparam>
/// <param name="file">The file.</param>
/// <param name="settings">The settings.</param>
/// <param name="result">The result.</param>
/// <returns><c>true</c> if [is valid json] [the specified settings]; otherwise, <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException">file</exception>
/// <exception cref="System.ArgumentNullException"></exception>
/// <exception cref="System.ArgumentNullException">settings</exception>
/// <exception cref="System.IO.FileNotFoundException">File could not be accessed</exception>
public static bool IsValidJson<T>(this FileInfo file, JsonSerializerSettings settings, [NotNullWhen(true)] out T? result)
{
if (file is null)
{
throw new ArgumentNullException(nameof(file));
}
if (File.Exists(file.FullName) == false)
{
throw new FileNotFoundException("File could not be accessed",fileName: file.FullName);
}
using var stream = file.OpenRead();
if (stream is null)
{
throw new ArgumentNullException(message:"Could not open the file and access the underlying file stream",paramName: nameof(file));
}
if (settings is null)
{
throw new ArgumentNullException(nameof(settings));
}
using (var reader = new StreamReader(stream))
using (var jsonReader = new JsonTextReader(reader))
{
var ser = JsonSerializer.Create(settings);
try
{
result = ser.Deserialize<T>(jsonReader);
}
catch { result = default; }
}
return result is not null;
}
/// <summary>
/// Determines whether file contains valid json using the specified settings and reads it into the output.
/// </summary>
/// <typeparam name="T">Type to convert into</typeparam>
/// <param name="file">The file.</param>
/// <param name="result">The result.</param>
/// <returns><c>true</c> if [is valid json] [the specified result]; otherwise, <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException">file</exception>
/// <exception cref="System.IO.FileNotFoundException">File could not be accessed</exception>
public static bool IsValidJson<T>(this FileInfo file, [NotNullWhen(true)] out T? result)
{
if (file is null)
{
throw new ArgumentNullException(nameof(file));
}
if (File.Exists(file.FullName) == false)
{
throw new FileNotFoundException("File could not be accessed",fileName: file.FullName);
}
JsonSerializerSettings settings =( JsonConvert.DefaultSettings?.Invoke()) ?? new JsonSerializerSettings() { DateTimeZoneHandling= DateTimeZoneHandling.Utc, DateFormatHandling= DateFormatHandling.IsoDateFormat };
settings.ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor;
return file.IsValidJson<T>(settings,out result);
}
}

Related

Read XML node using reflection c#

I have this function to read xml values and create an instance of a class.
Is there a way to combine linq and reflection to not specify the properties of the class and create the class with less code lines?
I try to avoid to modify this method if I add or remove some fields to Test_Class
/// <summary>
/// Get the values of a xml node and return a class of type Class_Test
/// </summary>
/// <param name="sXmlFileName">Path to xml file.</param>
/// <param name="sNodeName">Name of node to get values.</param>
/// <returns>Class_Test New class with the values set from XML.</returns>
public static Class_Test Func_ReadXMLNode(string sXmlFileName, string sNodeName)
{
//Load XML
XDocument xml_Document;
Class_Test result;
try
{
xml_Document = XDocument.Load(sXmlFileName);
//Read XML Section
var xmlValues = from r in xml_Document.Descendants(sNodeName)
select new Class_Test
{
sNetworkInterfaceName = Convert.ToString(r.Element("").Value),
iNetworkInterfacePort = Convert.ToInt32(r.Element("").Value),
sCertificateFile = Convert.ToString(r.Element("").Value),
sCertificateName = Convert.ToString(r.Element("").Value),
iKeepAliveSendingTime = Convert.ToInt32(r.Element("").Value),
iMaximumTimeWithoutKeepAlive = Convert.ToInt32(r.Element("").Value),
sSqlServer = Convert.ToString(r.Element("").Value),
sDatabase = Convert.ToString(r.Element("").Value),
iFtpRetries = Convert.ToInt32(r.Element("").Value),
sDetectionFilesDirectory = Convert.ToString(r.Element("").Value),
sImgDirectory = Convert.ToString(r.Element("").Value),
sLocalDirectory = Convert.ToString(r.Element("").Value),
sOffenceDirectory = Convert.ToString(r.Element("").Value),
sTmpDirectory = Convert.ToString(r.Element("").Value)
};
result = xmlValues.FirstOrDefault();
}
catch (IOException Exception1)
{
LogHelper.Func_WriteEventInLogFile(DateTime.Now.ToLocalTime(), enum_EventTypes.Error, "Func_ReadXMLConfig()", "Source = " + Exception1.Source.Replace("'", "''") + ", Message = " + Exception1.Message.Replace("'", "''"));
result = new Class_Test();
}
return result;
}
And this is Class_Text:
/// <summary>
/// Class of operation mode.
/// </summary>
public class Class_OperationMode
{
#region CONFIG_PARAMETERS
/// <summary>
/// Name of the network interface.
/// </summary>
public string sNetworkInterfaceName;
/// <summary>
/// Port of the network interface.
/// </summary>
public int iNetworkInterfacePort;
/// <summary>
/// Path to certificate file.
/// </summary>
public string sCertificateFile;
/// <summary>
/// Name of the certificate.
/// </summary>
public string sCertificateName;
/// <summary>
/// Time to keep alive the connection while sending data.
/// </summary>
public int iKeepAliveSendingTime;
/// <summary>
/// Time before timeout of the connection.
/// </summary>
public int iMaximumTimeWithoutKeepAlive;
/// <summary>
/// Database server instance.
/// </summary>
public string sSqlServer;
/// <summary>
/// Path to .mdf file of database.
/// </summary>
public string sDatabase;
/// <summary>
/// Max retries to try to connect to FTP Server.
/// </summary>
public int iFtpRetries;
/// <summary>
/// Path to detections files directory.
/// </summary>
public string sDetectionFilesDirectory;
/// <summary>
/// Path to images directory.
/// </summary>
public string sImgDirectory;
/// <summary>
/// Path to local directory.
/// </summary>
public string sLocalDirectory;
/// <summary>
/// Path to folder where save and retrieve offences.
/// </summary>
public string sOffenceDirectory;
/// <summary>
/// Path to temp directory.
/// </summary>
public string sTmpDirectory;
#endregion
UPDATE: Thanks to comments, I updated code to:
public static Class_Test Func_ReadXMLNode(string sXmlFileName, string sNodeName){
//Load XML
XDocument xml_Document;
Class_Test result;
try
{
xml_Document = XDocument.Load(sXmlFileName);
//Read XML Section
//Get xml values of descendants
XElement xmlValues = xml_Document.Descendants(sNodeName).FirstOrDefault();
//Create serializer
XmlSerializer serializer = new XmlSerializer(typeof(Class_Test));
//Deserialize
using (XmlReader reader = xmlValues.CreateReader())
{
result = (Class_Test)serializer.Deserialize(reader);
}
}
catch (IOException Exception1)
{
LogHelper.Func_WriteEventInLogFile(DateTime.Now.ToLocalTime(), enum_EventTypes.Error, "Func_ReadXMLConfig()", "Source = " + Exception1.Source.Replace("'", "''") + ", Message = " + Exception1.Message.Replace("'", "''"));
result = new Class_Test();
}
return result;
}
Thanks in advance. Best regards,
JoaquĆ­n
I think what youre looking for is serialization/deserialization. Theres lots of useful stuff in .net for handling xml serialization. Ive stolen this example from the docs (link underneath).
XmlSerializer serializer = new
XmlSerializer(typeof(OrderedItem));
// A FileStream is needed to read the XML document.
FileStream fs = new FileStream(filename, FileMode.Open);
XmlReader reader = XmlReader.Create(fs);
// Declare an object variable of the type to be deserialized.
OrderedItem i;
// Use the Deserialize method to restore the object's state.
i = (OrderedItem)serializer.Deserialize(reader);
fs.Close();
From https://msdn.microsoft.com/en-us/library/tz8csy73(v=vs.110).aspx

Handle multiple binding errors from ModelBindingException in NancyFX when binding to JSON in Request.Body

I have a post route that accepts some JSON payload in the request body.
Post["/myroute/}"] = _ =>
{
try
{
var model = this.Bind<MyModel>();
}
catch (ModelBindingException e)
{
//PropertyBindException list is empty here,
//so only the first exception can be handled...
}
}
If there are multiple invalid data types (i.e. if there are several int properties defined in MyModel, and a user posts strings for those properties), I would like pass back a nice list of these errors, similar to how would use ModelState dictionary in a vanilla ASP.NET MVC application.
How can I accomplish this type of exception handling when attempting to bind the JSON payload in the request body to my Model in NancyFX?
Update:
Looking through the DefaultBinder in the Nancy source here:
https://github.com/sloncho/Nancy/blob/master/src/Nancy/ModelBinding/DefaultBinder.cs
The problem I see is that in this block:
try
{
var bodyDeserializedModel = this.DeserializeRequestBody(bindingContext);
if (bodyDeserializedModel != null)
{
UpdateModelWithDeserializedModel(bodyDeserializedModel, bindingContext);
}
}
catch (Exception exception)
{
if (!bindingContext.Configuration.IgnoreErrors)
{
throw new ModelBindingException(modelType, innerException: exception);
}
}
The Deserialize call seems to be "all or nothing" and it is handled by a plain Exception, not a ModelBindException, so I cannot see any PropertyBindExceptions here either.
Should I be needing to implement something custom for this...?
Add your own custom body serializer that uses newtonsoft to ignore errors:
public class CustomBodyDeserializer : IBodyDeserializer
{
private readonly MethodInfo deserializeMethod = typeof(JavaScriptSerializer).GetMethod("Deserialize", BindingFlags.Instance | BindingFlags.Public);
private readonly JsonConfiguration jsonConfiguration;
private readonly GlobalizationConfiguration globalizationConfiguration;
/// <summary>
/// Initializes a new instance of the <see cref="JsonBodyDeserializer"/>,
/// with the provided <paramref name="environment"/>.
/// </summary>
/// <param name="environment">An <see cref="INancyEnvironment"/> instance.</param>
public CustomBodyDeserializer(INancyEnvironment environment)
{
this.jsonConfiguration = environment.GetValue<JsonConfiguration>();
this.globalizationConfiguration = environment.GetValue<GlobalizationConfiguration>();
}
/// <summary>
/// Whether the deserializer can deserialize the content type
/// </summary>
/// <param name="mediaRange">Content type to deserialize</param>
/// <param name="context">Current <see cref="BindingContext"/>.</param>
/// <returns>True if supported, false otherwise</returns>
public bool CanDeserialize(MediaRange mediaRange, BindingContext context)
{
return Json.IsJsonContentType(mediaRange);
}
/// <summary>
/// Deserialize the request body to a model
/// </summary>
/// <param name="mediaRange">Content type to deserialize</param>
/// <param name="bodyStream">Request body stream</param>
/// <param name="context">Current context</param>
/// <returns>Model instance</returns>
public object Deserialize(MediaRange mediaRange, Stream bodyStream, BindingContext context)
{
//var serializer = new JavaScriptSerializer(this.jsonConfiguration, this.globalizationConfiguration);
//serializer.RegisterConverters(this.jsonConfiguration.Converters, this.jsonConfiguration.PrimitiveConverters);
if (bodyStream.CanSeek)
{
bodyStream.Position = 0;
}
string bodyText;
using (var bodyReader = new StreamReader(bodyStream))
{
bodyText = bodyReader.ReadToEnd();
}
// var genericDeserializeMethod = this.deserializeMethod.MakeGenericMethod(context.DestinationType);
// var deserializedObject = genericDeserializeMethod.Invoke(serializer, new object[] { bodyText });
object deserializedObject = JsonConvert.DeserializeObject(bodyText, context.DestinationType, new JsonSerializerSettings
{
Error = HandleDeserializationError
});
return deserializedObject;
}
public void HandleDeserializationError(object sender, Newtonsoft.Json.Serialization.ErrorEventArgs errorArgs)
{
string currentError = errorArgs.ErrorContext.Error.Message;
errorArgs.ErrorContext.Handled = true;
}
}

How to write a "filter" stream wrapper for XML?

I have some large XML feed files with illegal characters in them (0x1 etc). The files are third-party, I cannot change the process for writing them.
I would like to process these files using an XmlReader, but it blows up on these illegal characters.
I could read the files, filter out the bad characters, save them, then process them... but this is a lot of I/O, and it seems like it should be unnecessary.
What I would like to do is something like this:
using(var origStream = File.OpenRead(fileName))
using(var cleanStream = new CleansedXmlStream(origStream))
using(var streamReader = new StreamReader(cleanStream))
using(var xmlReader = XmlReader.Create(streamReader))
{
//do stuff with reader
}
I tried inheriting from Stream, but when I got to implementing the Read(byte[] buffer, int offset, int count) I lost some confidence. After all, I was planning on removing characters, so it seemed the count would be off, and I'd have to translate each byte to a char which seemed expensive (especially on large files) and I was unclear how this would work with a Unicode encoding, but the answers to my questions were not intuitively obvious.
When googling for "c# stream wrapper" or "c# filter stream" I am not getting satisfactory results. It's possible I'm using the wrong words or describing the wrong concept, so I'm hoping the SO community can square me away.
Using the example above, what would CleansedXmlStream look like?
Here's what my first attempt looked like:
public class CleansedXmlStream : Stream
{
private readonly Stream _baseStream;
public CleansedXmlStream(Stream stream)
{
this._baseStream = stream;
}
public new void Dispose()
{
if (this._baseStream != null)
{
this._baseStream.Dispose();
}
base.Dispose();
}
public override bool CanRead
{
get { return this._baseStream.CanRead; }
}
public override bool CanSeek
{
get { return this._baseStream.CanSeek; }
}
public override bool CanWrite
{
get { return this._baseStream.CanWrite; }
}
public override long Length
{
get { return this._baseStream.Length; }
}
public override long Position
{
get { return this._baseStream.Position; }
set { this._baseStream.Position = value; }
}
public override void Flush()
{
this._baseStream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
//what does this look like?
throw new NotImplementedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
return this._baseStream.Seek(offset, origin);
}
public override void SetLength(long value)
{
this._baseStream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
}
Inspired by #CharlesMager's comment, I ended up not making a Stream, but rather a StreamReader like so:
public class CleanTextReader : StreamReader
{
private readonly ILog _logger;
public CleanTextReader(Stream stream, ILog logger) : base(stream)
{
this._logger = logger;
}
public CleanTextReader(Stream stream) : this(stream, LogManager.GetLogger<CleanTextReader>())
{
//nothing to do here.
}
/// <summary>
/// Reads a specified maximum of characters from the current stream into a buffer, beginning at the specified index.
/// </summary>
/// <returns>
/// The number of characters that have been read, or 0 if at the end of the stream and no data was read. The number
/// will be less than or equal to the <paramref name="count" /> parameter, depending on whether the data is available
/// within the stream.
/// </returns>
/// <param name="buffer">
/// When this method returns, contains the specified character array with the values between
/// <paramref name="index" /> and (<paramref name="index + count - 1" />) replaced by the characters read from the
/// current source.
/// </param>
/// <param name="index">The index of <paramref name="buffer" /> at which to begin writing. </param>
/// <param name="count">The maximum number of characters to read. </param>
/// <exception cref="T:System.ArgumentException">
/// The buffer length minus <paramref name="index" /> is less than
/// <paramref name="count" />.
/// </exception>
/// <exception cref="T:System.ArgumentNullException"><paramref name="buffer" /> is null. </exception>
/// <exception cref="T:System.ArgumentOutOfRangeException">
/// <paramref name="index" /> or <paramref name="count" /> is
/// negative.
/// </exception>
/// <exception cref="T:System.IO.IOException">An I/O error occurs, such as the stream is closed. </exception>
public override int Read(char[] buffer, int index, int count)
{
try
{
var rVal = base.Read(buffer, index, count);
var filteredBuffer = buffer.Select(x => XmlConvert.IsXmlChar(x) ? x : ' ').ToArray();
Buffer.BlockCopy(filteredBuffer, 0, buffer, 0, count);
return rVal;
}
catch (Exception ex)
{
this._logger.Error("Read(char[], int, int)", ex);
throw;
}
}
/// <summary>
/// Reads a maximum of <paramref name="count" /> characters from the current stream, and writes the data to
/// <paramref name="buffer" />, beginning at <paramref name="index" />.
/// </summary>
/// <returns>
/// The position of the underlying stream is advanced by the number of characters that were read into
/// <paramref name="buffer" />.The number of characters that have been read. The number will be less than or equal to
/// <paramref name="count" />, depending on whether all input characters have been read.
/// </returns>
/// <param name="buffer">
/// When this method returns, this parameter contains the specified character array with the values
/// between <paramref name="index" /> and (<paramref name="index" /> + <paramref name="count" /> -1) replaced by the
/// characters read from the current source.
/// </param>
/// <param name="index">The position in <paramref name="buffer" /> at which to begin writing.</param>
/// <param name="count">The maximum number of characters to read. </param>
/// <exception cref="T:System.ArgumentNullException"><paramref name="buffer" /> is null. </exception>
/// <exception cref="T:System.ArgumentException">
/// The buffer length minus <paramref name="index" /> is less than
/// <paramref name="count" />.
/// </exception>
/// <exception cref="T:System.ArgumentOutOfRangeException">
/// <paramref name="index" /> or <paramref name="count" /> is
/// negative.
/// </exception>
/// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.IO.TextReader" /> is closed. </exception>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
public override int ReadBlock(char[] buffer, int index, int count)
{
try
{
var rVal = base.ReadBlock(buffer, index, count);
var filteredBuffer = buffer.Select(x => XmlConvert.IsXmlChar(x) ? x : ' ').ToArray();
Buffer.BlockCopy(filteredBuffer, 0, buffer, 0, count);
return rVal;
}
catch (Exception ex)
{
this._logger.Error("ReadBlock(char[], in, int)", ex);
throw;
}
}
/// <summary>
/// Reads the stream from the current position to the end of the stream.
/// </summary>
/// <returns>
/// The rest of the stream as a string, from the current position to the end. If the current position is at the end of
/// the stream, returns an empty string ("").
/// </returns>
/// <exception cref="T:System.OutOfMemoryException">
/// There is insufficient memory to allocate a buffer for the returned
/// string.
/// </exception>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
public override string ReadToEnd()
{
var chars = new char[4096];
int len;
var sb = new StringBuilder(4096);
while ((len = Read(chars, 0, chars.Length)) != 0)
{
sb.Append(chars, 0, len);
}
return sb.ToString();
}
}
My unit test looks like this:
[TestMethod]
public void CleanTextReaderCleans()
{
//arrange
var originalString = "The quick brown fox jumped over the lazy dog.";
var badChars = new string(new[] {(char) 0x1});
var concatenated = string.Concat(badChars, originalString);
//act
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(concatenated)))
{
using (var reader = new CleanTextReader(stream))
{
var newString = reader.ReadToEnd().Trim();
//assert
Assert.IsTrue(originalString.Equals(newString));
}
}
}
... and usage looks like this:
using(var origStream = File.OpenRead(fileName))
using(var streamReader = new CleanTextReader(origStream))
using(var xmlReader = XmlReader.Create(streamReader))
{
//do stuff with reader
}
If anyone has suggestions for improvements, I'd be happy to hear them.
I tried #JeremyHolovacs stream implementation but it still was not sufficient for my use case:
using (var fstream = File.OpenRead(dlpath))
{
using (var zstream = new GZipStream(fstream, CompressionMode.Decompress))
{
using (var xstream = new CleanTextReader(zstream))
{
var ser = new XmlSerializer(typeof(MyType));
prods = ser.Deserialize(XmlReader.Create(xstream, new XmlReaderSettings() { CheckCharacters = false })) as MyType;
}
}
}
Somehow not all relevant overloads must have been implemented.
I adapted the class as follows and it works as expected:
public class CleanTextReader : StreamReader
{
public CleanTextReader(Stream stream) : base(stream)
{
}
public override int Read()
{
var val = base.Read();
return XmlConvert.IsXmlChar((char)val) ? val : (char)' ';
}
public override int Read(char[] buffer, int index, int count)
{
var ret = base.Read(buffer, index, count);
for (int i=0; i<ret; i++)
{
int idx = index + i;
if (!XmlConvert.IsXmlChar(buffer[idx]))
buffer[idx] = ' ';
}
return ret;
}
public override int ReadBlock(char[] buffer, int index, int count)
{
var ret = base.ReadBlock(buffer, index, count);
for (int i = 0; i < ret; i++)
{
int idx = index + i;
if (!XmlConvert.IsXmlChar(buffer[idx]))
buffer[idx] = ' ';
}
return ret;
}
}

write list of objects to a file

I've got a class salesman in the following format:
class salesman
{
public string name, address, email;
public int sales;
}
I've got another class where the user inputs name, address, email and sales.
This input is then added to a list
List<salesman> salesmanList = new List<salesman>();
After the user has input as many salesman to the list as they like, they have the option to save the list to a file of their choice (which I can limit to .xml or .txt(which ever is more appropriate)).
How would I add this list to the file?
Also this file needs to be re-read back into a list if the user wishes to later view the records.
Something like this would work. this uses a binary format (the fastest for loading) but the same code would apply to xml with a different serializer.
using System.IO;
[Serializable]
class salesman
{
public string name, address, email;
public int sales;
}
class Program
{
static void Main(string[] args)
{
List<salesman> salesmanList = new List<salesman>();
string dir = #"c:\temp";
string serializationFile = Path.Combine(dir, "salesmen.bin");
//serialize
using (Stream stream = File.Open(serializationFile, FileMode.Create))
{
var bformatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
bformatter.Serialize(stream, salesmanList);
}
//deserialize
using (Stream stream = File.Open(serializationFile, FileMode.Open))
{
var bformatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
List<salesman> salesman = (List<salesman>)bformatter.Deserialize(stream);
}
}
}
I just wrote a blog post on saving an object's data to Binary, XML, or Json; well writing an object or list of objects to a file that is. Here are the functions to do it in the various formats. See my blog post for more details.
Binary
/// <summary>
/// Writes the given object instance to a binary file.
/// <para>Object type (and all child types) must be decorated with the [Serializable] attribute.</para>
/// <para>To prevent a variable from being serialized, decorate it with the [NonSerialized] attribute; cannot be applied to properties.</para>
/// </summary>
/// <typeparam name="T">The type of object being written to the XML file.</typeparam>
/// <param name="filePath">The file path to write the object instance to.</param>
/// <param name="objectToWrite">The object instance to write to the XML file.</param>
/// <param name="append">If false the file will be overwritten if it already exists. If true the contents will be appended to the file.</param>
public static void WriteToBinaryFile<T>(string filePath, T objectToWrite, bool append = false)
{
using (Stream stream = File.Open(filePath, append ? FileMode.Append : FileMode.Create))
{
var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
binaryFormatter.Serialize(stream, objectToWrite);
}
}
/// <summary>
/// Reads an object instance from a binary file.
/// </summary>
/// <typeparam name="T">The type of object to read from the XML.</typeparam>
/// <param name="filePath">The file path to read the object instance from.</param>
/// <returns>Returns a new instance of the object read from the binary file.</returns>
public static T ReadFromBinaryFile<T>(string filePath)
{
using (Stream stream = File.Open(filePath, FileMode.Open))
{
var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
return (T)binaryFormatter.Deserialize(stream);
}
}
XML
Requires the System.Xml assembly to be included in your project.
/// <summary>
/// Writes the given object instance to an XML file.
/// <para>Only Public properties and variables will be written to the file. These can be any type though, even other classes.</para>
/// <para>If there are public properties/variables that you do not want written to the file, decorate them with the [XmlIgnore] attribute.</para>
/// <para>Object type must have a parameterless constructor.</para>
/// </summary>
/// <typeparam name="T">The type of object being written to the file.</typeparam>
/// <param name="filePath">The file path to write the object instance to.</param>
/// <param name="objectToWrite">The object instance to write to the file.</param>
/// <param name="append">If false the file will be overwritten if it already exists. If true the contents will be appended to the file.</param>
public static void WriteToXmlFile<T>(string filePath, T objectToWrite, bool append = false) where T : new()
{
TextWriter writer = null;
try
{
var serializer = new XmlSerializer(typeof(T));
writer = new StreamWriter(filePath, append);
serializer.Serialize(writer, objectToWrite);
}
finally
{
if (writer != null)
writer.Close();
}
}
/// <summary>
/// Reads an object instance from an XML file.
/// <para>Object type must have a parameterless constructor.</para>
/// </summary>
/// <typeparam name="T">The type of object to read from the file.</typeparam>
/// <param name="filePath">The file path to read the object instance from.</param>
/// <returns>Returns a new instance of the object read from the XML file.</returns>
public static T ReadFromXmlFile<T>(string filePath) where T : new()
{
TextReader reader = null;
try
{
var serializer = new XmlSerializer(typeof(T));
reader = new StreamReader(filePath);
return (T)serializer.Deserialize(reader);
}
finally
{
if (reader != null)
reader.Close();
}
}
Json
You must include a reference to Newtonsoft.Json assembly, which can be obtained from the Json.NET NuGet Package.
/// <summary>
/// Writes the given object instance to a Json file.
/// <para>Object type must have a parameterless constructor.</para>
/// <para>Only Public properties and variables will be written to the file. These can be any type though, even other classes.</para>
/// <para>If there are public properties/variables that you do not want written to the file, decorate them with the [JsonIgnore] attribute.</para>
/// </summary>
/// <typeparam name="T">The type of object being written to the file.</typeparam>
/// <param name="filePath">The file path to write the object instance to.</param>
/// <param name="objectToWrite">The object instance to write to the file.</param>
/// <param name="append">If false the file will be overwritten if it already exists. If true the contents will be appended to the file.</param>
public static void WriteToJsonFile<T>(string filePath, T objectToWrite, bool append = false) where T : new()
{
TextWriter writer = null;
try
{
var contentsToWriteToFile = JsonConvert.SerializeObject(objectToWrite);
writer = new StreamWriter(filePath, append);
writer.Write(contentsToWriteToFile);
}
finally
{
if (writer != null)
writer.Close();
}
}
/// <summary>
/// Reads an object instance from an Json file.
/// <para>Object type must have a parameterless constructor.</para>
/// </summary>
/// <typeparam name="T">The type of object to read from the file.</typeparam>
/// <param name="filePath">The file path to read the object instance from.</param>
/// <returns>Returns a new instance of the object read from the Json file.</returns>
public static T ReadFromJsonFile<T>(string filePath) where T : new()
{
TextReader reader = null;
try
{
reader = new StreamReader(filePath);
var fileContents = reader.ReadToEnd();
return JsonConvert.DeserializeObject<T>(fileContents);
}
finally
{
if (reader != null)
reader.Close();
}
}
Example
// Write the list of salesman objects to file.
WriteToXmlFile<List<salesman>>("C:\salesmen.txt", salesmanList);
// Read the list of salesman objects from the file back into a variable.
List<salesman> salesmanList = ReadFromXmlFile<List<salesman>>("C:\salesmen.txt");
If you want to use JSON then using Json.NET is usually the best way to go.
If for some reason you are unable to use Json.NET you can use the built in JSON support found in .NET.
You will need to include the following using statement and add a reference for System.Web.Extentsions.
using System.Web.Script.Serialization;
Then you would use these to Serialize and Deserialize your object.
//Deserialize JSON to your Object
YourObject obj = new JavaScriptSerializer().Deserialize<YourObject>("File Contents");
//Serialize your object to JSON
string sJSON = new JavaScriptSerializer().Serialize(YourObject);
https://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer_methods(v=vs.110).aspx
If you want xml serialization, you can use the built-in serializer. To achieve this, add [Serializable] flag to the class:
[Serializable()]
class salesman
{
public string name, address, email;
public int sales;
}
Then, you could override the "ToString()" method which converts the data into xml string:
public override string ToString()
{
string sData = "";
using (MemoryStream oStream = new MemoryStream())
{
XmlSerializer oSerializer = new XmlSerializer(this.GetType());
oSerializer.Serialize(oStream, this);
oStream.Position = 0;
sData = Encoding.UTF8.GetString(oStream.ToArray());
}
return sData;
}
Then just create a method that writes this.ToString() into a file.
UPDATE
The mentioned above will serialize single entry as xml. If you need the whole list to be serialized, the idea would be a bit different. In this case you'd employ the fact that lists are serializable if their contents are serializable and use the serialization in some outer class.
Example code:
[Serializable()]
class salesman
{
public string name, address, email;
public int sales;
}
class salesmenCollection
{
List<salesman> salesmanList;
public void SaveTo(string path){
System.IO.File.WriteAllText (path, this.ToString());
}
public override string ToString()
{
string sData = "";
using (MemoryStream oStream = new MemoryStream())
{
XmlSerializer oSerializer = new XmlSerializer(this.GetType());
oSerializer.Serialize(oStream, this);
oStream.Position = 0;
sData = Encoding.UTF8.GetString(oStream.ToArray());
}
return sData;
}
}

XML Deserialize in Windows Phone C#

i'm working on WP Application for news portal, I need to parse various feeds. Standard feed looks like this:
<feed>
<items>
<item>
...
</item>
<item>
...
</item>
</items>
</feed>
and I use this code to deserialize it:
XDocument document = XDocument.Load(xmlStream);
XmlSerializer serializer = new XmlSerializer(typeof(News));
News MainPage = (News)serializer.Deserialize(document.CreateReader());
Dispatcher.BeginInvoke(() => MainPageListBox.ItemsSource = MainPage.DataSet;);
the News class looks like this:
[XmlRoot("feed")]
public class News
{
[XmlArray("items")]
[XmlArrayItem("item")]
public ObservableCollection<NewsItem> DataSet{ get; set; }
}
This work fine in every feed having this structure with one <items> section. But I also have feed with multiple sections, for example:
<feed>
<items section="part 1">
<item>
...
</item>
<item>
...
</item>
</items>
<items section="part 2">
<item>
...
</item>
<item>
...
</item>
</items>
</feed>
If I use C# code above, only first <items> section is parsed, others are ignored. I need to create separate List or ObservableCollection for each <items> section in single XML feed.
Can anyone help me with this? Thanks a lot.
Classes:
[XmlRoot("feed")]
public class News
{
[XmlArray("items")]
public List<NewsItemCollection> DataSet { get; set; }
public News()
{
DataSet = new List<NewsItemCollection>();
}
}
public class NewsItemCollection
{
[XmlAttribute("section")]
public string Section { get; set; }
[XmlElement("item")]
public ObservableCollection<NewsItem> Items { get; set; }
public NewsItemCollection()
{
Items = new ObservableCollection<NewsItem>();
}
}
public class NewsItem
{
public string Title { get; set; }
}
Some helper classes of mine for serializing/deserializing xml:
public static class ObjectExtensions
{
/// <summary>
/// <para>Serializes the specified System.Object and writes the XML document</para>
/// <para>to the specified file.</para>
/// </summary>
/// <typeparam name="T">This item's type</typeparam>
/// <param name="item">This item</param>
/// <param name="fileName">The file to which you want to write.</param>
/// <returns>true if successful, otherwise false.</returns>
public static bool XmlSerialize<T>(this T item, string fileName)
{
return item.XmlSerialize(fileName, true);
}
/// <summary>
/// <para>Serializes the specified System.Object and writes the XML document</para>
/// <para>to the specified file.</para>
/// </summary>
/// <typeparam name="T">This item's type</typeparam>
/// <param name="item">This item</param>
/// <param name="fileName">The file to which you want to write.</param>
/// <param name="removeNamespaces">
/// <para>Specify whether to remove xml namespaces.</para>para>
/// <para>If your object has any XmlInclude attributes, then set this to false</para>
/// </param>
/// <returns>true if successful, otherwise false.</returns>
public static bool XmlSerialize<T>(this T item, string fileName, bool removeNamespaces)
{
object locker = new object();
XmlSerializerNamespaces xmlns = new XmlSerializerNamespaces();
xmlns.Add(string.Empty, string.Empty);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
lock (locker)
{
using (XmlWriter writer = XmlWriter.Create(fileName, settings))
{
if (removeNamespaces)
{
xmlSerializer.Serialize(writer, item, xmlns);
}
else { xmlSerializer.Serialize(writer, item); }
writer.Close();
}
}
return true;
}
/// <summary>
/// Serializes the specified System.Object and returns the serialized XML
/// </summary>
/// <typeparam name="T">This item's type</typeparam>
/// <param name="item">This item</param>
/// <returns>Serialized XML for specified System.Object</returns>
public static string XmlSerialize<T>(this T item)
{
return item.XmlSerialize(true);
}
/// <summary>
/// Serializes the specified System.Object and returns the serialized XML
/// </summary>
/// <typeparam name="T">This item's type</typeparam>
/// <param name="item">This item</param>
/// <param name="removeNamespaces">
/// <para>Specify whether to remove xml namespaces.</para>para>
/// <para>If your object has any XmlInclude attributes, then set this to false</para>
/// </param>
/// <returns>Serialized XML for specified System.Object</returns>
public static string XmlSerialize<T>(this T item, bool removeNamespaces)
{
object locker = new object();
XmlSerializerNamespaces xmlns = new XmlSerializerNamespaces();
xmlns.Add(string.Empty, string.Empty);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
lock (locker)
{
StringBuilder stringBuilder = new StringBuilder();
using (StringWriter stringWriter = new StringWriter(stringBuilder))
{
using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter, settings))
{
if (removeNamespaces)
{
xmlSerializer.Serialize(xmlWriter, item, xmlns);
}
else { xmlSerializer.Serialize(xmlWriter, item); }
return stringBuilder.ToString();
}
}
}
}
}
public static class StringExtensions
{
/// <summary>
/// Deserializes the XML data contained by the specified System.String
/// </summary>
/// <typeparam name="T">The type of System.Object to be deserialized</typeparam>
/// <param name="s">The System.String containing XML data</param>
/// <returns>The System.Object being deserialized.</returns>
public static T XmlDeserialize<T>(this string s)
{
var locker = new object();
var stringReader = new StringReader(s);
var reader = new XmlTextReader(stringReader);
try
{
var xmlSerializer = new XmlSerializer(typeof(T));
lock (locker)
{
var item = (T)xmlSerializer.Deserialize(reader);
reader.Close();
return item;
}
}
catch
{
return default(T);
}
finally
{
reader.Close();
}
}
}
test run:
News news = new News();
news.DataSet.Add(new NewsItemCollection
{
Section = "Section1",
Items = new ObservableCollection<NewsItem>
{
new NewsItem { Title = "Test1.1" },
new NewsItem { Title = "Test1.2" }
}
});
news.DataSet.Add(new NewsItemCollection
{
Section = "Section2",
Items = new ObservableCollection<NewsItem>
{
new NewsItem { Title = "Test2.1" },
new NewsItem { Title = "Test2.2" }
}
});
var serialized = news.XmlSerialize();
var deserialized = serialized.XmlDeserialize<News>();
Have fun!

Categories