Best way to unit test classes using streams? - c#

I am currently writing some code to try to experiment with separating and abstracting two parts of our storage strategies at work. We currently use JSON format stored into a file and then retrieve it as our persistent storage. I am trying to experiment with separating the two concepts:
1) Concept one keeps the serialization separate from the storage type
2) Concept two keeps the storage type separate from the serialization strategy.
I found a good way that works doing some research on various threads, such as using TextWriter/TextReader instead of directly using Files so that any Stream type can be used (FileStream/MemoryStream/etc) so that the unit tests can be done without files. However, I am running into a problem since the TextWriter/TextReader classes which wrap the streams automatically close and dispose of the streams when they are themselves disposed, which is what I want in practice, but gets me stuck in unit testing.
Here is the code I have so far... this is for concept 1, the serialization process. Here are the interfaces for it:
/// <summary>
/// Interface for a serializer which reads from a stream and creates a type
/// </summary>
public interface IInSerializer
{
/// <summary>
/// Load type from a stream
/// </summary>
/// <param name="reader"></param>
/// <returns></returns>
bool Load(TextReader reader);
}
/// <summary>
/// Interface for writing a type out into a stream
/// </summary>
public interface IOutSerializer
{
/// <summary>
/// Save to the stream
/// </summary>
/// <param name="writer"></param>
/// <returns></returns>
bool Save(TextWriter writer);
}
/// <summary>
/// Helper interface which provides interface <see cref="IInSerializer"/>
/// and <see cref="IOutSerializer"/> for both reading/writing
/// </summary>
public interface IInOutSerializer : IInSerializer, IOutSerializer
{
}
Here is an abstract implementation of the serializer for JSON format:
/// <summary>
/// Implementation of <see cref="IInOutSerializer"/> which serializes into JSON format
/// </summary>
/// <typeparam name="T">Type to be serialized</typeparam>
public abstract class JSONSerializer<T> : IInOutSerializer
{
/// <summary>
/// Source of serialization
/// </summary>
public T Source { get; set; }
/// <summary>
/// Provided by very specific type to load the Jobject into type T
/// </summary>
/// <param name="jObject"></param>
/// <returns></returns>
protected abstract bool LoadJObject(JObject jObject);
/// <summary>
/// Provided by very specific type to save type T into a Jobject
/// </summary>
/// <param name="jObject"></param>
/// <returns></returns>
protected abstract bool Serialize(JObject jObject);
/// <summary>
/// <see cref="IInOutSerializer.Load"/>
/// </summary>
/// <param name="reader"></param>
/// <returns></returns>
public bool Load(TextReader reader)
{
using (var json = new JsonTextReader(reader))
{
var jObject = JToken.ReadFrom(json) as JObject;
if (jObject != null)
return LoadJObject(jObject);
}
return false;
}
/// <summary>
/// <see cref="IInOutSerializer.Save"/>
/// </summary>
/// <param name="writer"></param>
/// <returns></returns>
public bool Save(TextWriter writer)
{
var jObject = new JObject();
if (Serialize(jObject))
{
using (var json = new JsonTextWriter(writer))
{
json.Formatting = Formatting.Indented;
jObject.WriteTo(json);
return true;
}
}
return false;
}
}
And here is one of the concrete types for serializing my class MetroLineDetails:
public class MetroLineJSONSerializationStrategy : JSONSerializer<MetroLineDetails>
{
private class MetroLineHelper : IMetroLine, IMetroLineWritable
{
public string DestinationStation
{
get;
set;
}
public Color LineColor
{
get;
set;
}
public char LineLetter
{
get;
set;
}
public string Name
{
get;
set;
}
public bool SaturdayService
{
get;
set;
}
public string SourceStation
{
get;
set;
}
public bool SundayHolidayService
{
get;
set;
}
public static explicit operator MetroLineDetails(MetroLineHelper source)
{
return new MetroLineDetails(source.Name, source.LineColor, source.SourceStation, source.DestinationStation, source.SaturdayService, source.SundayHolidayService);
}
}
protected override bool LoadJObject(JObject jObject)
{
var helper = new MetroLineHelper();
jObject.Read(nameof(MetroLineDetails.Name), (t) => (string)t, (v) => helper.Name = v);
jObject.Read(nameof(MetroLineDetails.LineLetter), (t) => (char)t, (v) => helper.LineLetter = v);
jObject.Read(nameof(MetroLineDetails.SourceStation), (t) => (string)t, (v) => helper.SourceStation = v);
jObject.Read(nameof(MetroLineDetails.DestinationStation), (t) => (string)t, (v) => helper.DestinationStation = v);
jObject.Read(nameof(MetroLineDetails.SaturdayService), (t) => (bool)t, (v) => helper.SaturdayService = v);
jObject.Read(nameof(MetroLineDetails.SundayHolidayService), (t) => (bool)t, (v) => helper.SundayHolidayService = v);
var color = jObject.Read(nameof(MetroLineDetails.LineColor), (t) => (JObject)t);
helper.LineColor = color.ToColor();
Source = (MetroLineDetails)helper;
return true;
}
protected override bool Serialize(JObject jObject)
{
jObject.Add(nameof(MetroLineDetails.Name), Source.Name);
jObject.Add(nameof(MetroLineDetails.LineLetter), Source.LineLetter);
jObject.Add(nameof(MetroLineDetails.SourceStation), Source.SourceStation);
jObject.Add(nameof(MetroLineDetails.DestinationStation), Source.DestinationStation);
jObject.Add(nameof(MetroLineDetails.SaturdayService), Source.SaturdayService);
jObject.Add(nameof(MetroLineDetails.SundayHolidayService), Source.SundayHolidayService);
jObject.Add(nameof(MetroLineDetails.LineColor), Source.LineColor.ToJObject());
return true;
}
}
And now here are my storage type interfaces:
/// <summary>
/// Interface for the storage medium
/// </summary>
public interface IStorageMedium
{
/// <summary>
/// Save the information in the serializer
/// </summary>
/// <param name="serializer"></param>
void Save(IOutSerializer serializer);
/// <summary>
/// Load the information to the serializer
/// </summary>
/// <param name="serializer"></param>
void Load(IInSerializer serializer);
}
And the type specifically for files:
/// <summary>
/// Implementation of <see cref="IStorageMedium"/> which stores into a file
/// </summary>
public class FileStorageMedium : IStorageMedium
{
private readonly string _fileName;
public FileStorageMedium(string fileName)
{
_fileName = fileName;
}
public void Save(IOutSerializer serializer)
{
using (var stream = new FileStream(_fileName, FileMode.Truncate))
{
using (var writer = new StreamWriter(stream))
{
serializer.Save(writer);
}
}
}
public void Load(IInSerializer serializer)
{
using (var stream = new FileStream(_fileName, FileMode.Open))
{
using (var reader = new StreamReader(stream))
{
serializer.Load(reader);
}
}
}
}
As you can see in each layer I want to follow best practices and make sure each method closes and flushes the stream for the caller and not leave things open for the sake of unit testing (I know I could probably change the code to not close the streams, but I don't think that is appropriate).
So, now, using the ideas I've found on the forums to not have anything tied specifically to file streams to help with unit testing, I'm still running into problems finding the best way to unit test this. Here is the unit test I am trying to write:
[TestClass]
public class MetroLine
{
[TestMethod]
public void TestSerialize()
{
var serializer = new MetroLineJSONSerializationStrategy();
serializer.Source = new MetroLineDetails("A", Colors.Blue, "LA Union Station", "San Bernardino", true, true);
using (var stream = new MemoryStream())
{
var writer = new StreamWriter(stream);
serializer.Save(writer);
stream.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(stream))
{
var text = reader.ReadToEnd();
}
}
}
}
The stream is closed no matter what I do in the serializer.Save() call since that method uses a disposable which closes the stream (as I believe it should to prevent leaks). The problem is, I can no longer unit test the stream in any way to test whether any of this works. I get exceptions thrown saying you cannot access closed streams anymore, which makes sense. But how can I test the contents of my stream in any meaningful way?

I found GetBuffer on the MemoryStream which allows me to convert the raw buffer into a string and I can unit test the actual JSON blob however I want... here is what I wrote:
[TestMethod]
public void TestSerialize()
{
var serializer = new MetroLineJSONSerializationStrategy();
serializer.Source = new MetroLineDetails("Inland Empire Line", Colors.Blue, 'A', "LA Union Station", "San Bernardino", true, true);
using (var stream = new MemoryStream())
{
using (var writer = new StreamWriter(stream))
{
serializer.Save(writer);
}
var bytes = stream.GetBuffer();
var json = System.Text.Encoding.UTF8.GetString(bytes);
Assert.AreEqual('{', json[0]);
}
}
My hopes are someone will find this useful!

Related

Deserializing received Json-Rpc data via Websocket API

I'm trying to figure out a way to create a generic Websocket JsonRpc client.
After connecting I'm starting a loop to listen for data coming from the WebSocket API and sending that data to the event as a string. I'd like to return a generic JsonRpcResponse<T> object instead but not sure how or if it's possible.
Where JsonRpcResponse has an Id and Method fields as defined in the spec and T is a specific type depending on the data received.
From what I've seen there's no way to use generics here since I'd have to call another event with a non-generic type or object to pass that data along.
public event Func<string, Task>? OnDataReceived;
public async Task ConnectAsync()
{
Uri uri = new Uri(_url);
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(3000);
await _client.ConnectAsync(uri, cancellationTokenSource.Token);
// Start listening for data
await Task.Factory.StartNew(async () =>
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
while (_client.State == WebSocketState.Open && !cancellationTokenSource.IsCancellationRequested)
{
var receivedData = await ReadStringAsync(cancellationTokenSource.Token);
if (OnDataReceived != null)
{
await OnDataReceived.Invoke(receivedData);
}
}
};
}
How about this:
namespace GenericJsonResponse {
/// <summary>
/// Desired returned type
/// </summary>
/// <typeparam name="T"></typeparam>
public class JsonResponse<T> {
public T? Data { get; set; }
public int Id { get; set; } = 0;
public string Method { get; set; } = string.Empty;
/// <summary>
/// Convert string to anew instance
/// </summary>
/// <param name="json"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
/// <remarks>will have to add to each sub class</remarks>
public static T FromJson(string json) {
throw new NotImplementedException();
}
}
/// <summary>
/// Generic socket listener / sub class per type
/// </summary>
/// <typeparam name="T"></typeparam>
public class GenericSocketListener<T> {
/// <summary>
/// exposes event with generic response
/// </summary>
public event Func<string, Task<JsonResponse<T>>>? OnDataReceived;
/// <summary>
/// Listen to socket
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
public virtual async Task Listen( CancellationToken token) {
string data = string.Empty;
while (! token.IsCancellationRequested) {
// listen... and get typed result, save string to data
JsonResponse<T> result = await OnDataReceived.Invoke(data);
}
}
}
/// <summary>
/// Dummy poco
/// </summary>
public class Person {
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
}
/// <summary>
/// Class that will listen to people
/// </summary>
public class PersonSocketListener : GenericSocketListener<Person> {
}
}

Mongo C# driver toJson() DateTime

I have data in mongo which is like this:
"trd" : ISODate("2003-12-08T00:00:00Z")
Now, I am doing getting of data from Mongo as BsonDocument like this:
var builder = Builders<BsonDocument>.Filter;
var filter = builder.Eq("wsid", id);
var mongoListBatch = _client.GetManyByFilter(filter, "directory");
JsonWriterSettings settings = new JsonWriterSettings();
settings.OutputMode = JsonOutputMode.Strict;
var lists = mongoListBatch.ToJson(settings);
The problem is that I am expecting to get DateTime object like this inside the json:
"transactiedatum": "23-02-1993"
but instead, im getting:
"transactiedatum": {
"$date": 1070841600000
}
First of all, its unix time format, second, there is change in the json structure. How can I handle this?
We had similar problem. This sounds complicated, but its not that kind of big of a deal.
So, first of all, here is the link to Mongo C# drive on github:
MongoC#Driver
For you, these are 2 important links in there:
Bson Extension Methods
JsonWriter
JsonWriterContext
What we wanted to achieve is that we don't want representation in our Json string to be in unix timestamp format, so we copied these classes into our project with different names, so, here are all of them:
Extension Class:
using System;
using System.IO;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
using MongoDB.Bson;
namespace Fishing.MongoDB.Serializers
{
public static class MyJsonWriterSettings
{
/// <summary>
/// Serializes an object to a BSON byte array.
/// </summary>
/// <typeparam name="TNominalType">The nominal type of the object.</typeparam>
/// <param name="obj">The object.</param>
/// <param name="serializer">The serializer.</param>
/// <param name="writerSettings">The writer settings.</param>
/// <param name="configurator">The serialization context configurator.</param>
/// <param name="args">The serialization args.</param>
/// <returns>A BSON byte array.</returns>
public static byte[] ToBson<TNominalType>(
this TNominalType obj,
IBsonSerializer<TNominalType> serializer = null,
BsonBinaryWriterSettings writerSettings = null,
Action<BsonSerializationContext.Builder> configurator = null,
BsonSerializationArgs args = default(BsonSerializationArgs)
)
{
return ToBson(obj, typeof(TNominalType), writerSettings, serializer, configurator, args);
}
/// <summary>
/// Serializes an object to a BSON byte array.
/// </summary>
/// <param name="obj">The object.</param>
/// <param name="nominalType">The nominal type of the object..</param>
/// <param name="writerSettings">The writer settings.</param>
/// <param name="serializer">The serializer.</param>
/// <param name="configurator">The serialization context configurator.</param>
/// <param name="args">The serialization args.</param>
/// <returns>A BSON byte array.</returns>
/// <exception cref="System.ArgumentNullException">nominalType</exception>
/// <exception cref="System.ArgumentException">serializer</exception>
public static byte[] ToBson(
this object obj,
Type nominalType,
BsonBinaryWriterSettings writerSettings = null,
IBsonSerializer serializer = null,
Action<BsonSerializationContext.Builder> configurator = null,
BsonSerializationArgs args = default(BsonSerializationArgs))
{
if (nominalType == null)
{
throw new ArgumentNullException("nominalType");
}
if (serializer == null)
{
serializer = BsonSerializer.LookupSerializer(nominalType);
}
if (serializer.ValueType != nominalType)
{
var message = string.Format("Serializer type {0} value type does not match document types {1}.", serializer.GetType().FullName, nominalType.FullName);
throw new ArgumentException(message, "serializer");
}
using (var memoryStream = new MemoryStream())
{
using (var bsonWriter = new BsonBinaryWriter(memoryStream, writerSettings ?? BsonBinaryWriterSettings.Defaults))
{
var context = BsonSerializationContext.CreateRoot(bsonWriter, configurator);
args.NominalType = nominalType;
serializer.Serialize(context, args, obj);
}
return memoryStream.ToArray();
}
}
/// <summary>
/// Serializes an object to a BsonDocument.
/// </summary>
/// <typeparam name="TNominalType">The nominal type of the object.</typeparam>
/// <param name="obj">The object.</param>
/// <param name="serializer">The serializer.</param>
/// <param name="configurator">The serialization context configurator.</param>
/// <param name="args">The serialization args.</param>
/// <returns>A BsonDocument.</returns>
public static BsonDocument ToBsonDocument<TNominalType>(
this TNominalType obj,
IBsonSerializer<TNominalType> serializer = null,
Action<BsonSerializationContext.Builder> configurator = null,
BsonSerializationArgs args = default(BsonSerializationArgs))
{
return ToBsonDocument(obj, typeof(TNominalType), serializer, configurator, args);
}
/// <summary>
/// Serializes an object to a BsonDocument.
/// </summary>
/// <param name="obj">The object.</param>
/// <param name="nominalType">The nominal type of the object.</param>
/// <param name="serializer">The serializer.</param>
/// <param name="configurator">The serialization context configurator.</param>
/// <param name="args">The serialization args.</param>
/// <returns>A BsonDocument.</returns>
/// <exception cref="System.ArgumentNullException">nominalType</exception>
/// <exception cref="System.ArgumentException">serializer</exception>
public static BsonDocument ToBsonDocument(
this object obj,
Type nominalType,
IBsonSerializer serializer = null,
Action<BsonSerializationContext.Builder> configurator = null,
BsonSerializationArgs args = default(BsonSerializationArgs))
{
if (nominalType == null)
{
throw new ArgumentNullException("nominalType");
}
if (obj == null)
{
return null;
}
if (serializer == null)
{
var bsonDocument = obj as BsonDocument;
if (bsonDocument != null)
{
return bsonDocument; // it's already a BsonDocument
}
var convertibleToBsonDocument = obj as IConvertibleToBsonDocument;
if (convertibleToBsonDocument != null)
{
return convertibleToBsonDocument.ToBsonDocument(); // use the provided ToBsonDocument method
}
serializer = BsonSerializer.LookupSerializer(nominalType);
}
if (serializer.ValueType != nominalType)
{
var message = string.Format("Serializer type {0} value type does not match document types {1}.", serializer.GetType().FullName, nominalType.FullName);
throw new ArgumentException(message, "serializer");
}
// otherwise serialize into a new BsonDocument
var document = new BsonDocument();
using (var bsonWriter = new BsonDocumentWriter(document))
{
var context = BsonSerializationContext.CreateRoot(bsonWriter, configurator);
args.NominalType = nominalType;
serializer.Serialize(context, args, obj);
}
return document;
}
/// <summary>
/// Serializes an object to a JSON string.
/// </summary>
/// <typeparam name="TNominalType">The nominal type of the object.</typeparam>
/// <param name="obj">The object.</param>
/// <param name="writerSettings">The JsonWriter settings.</param>
/// <param name="serializer">The serializer.</param>
/// <param name="configurator">The serializastion context configurator.</param>
/// <param name="args">The serialization args.</param>
/// <returns>
/// A JSON string.
/// </returns>
public static string ToMyJson<TNominalType>(
this TNominalType obj,
JsonWriterSettings writerSettings = null,
IBsonSerializer<TNominalType> serializer = null,
Action<BsonSerializationContext.Builder> configurator = null,
BsonSerializationArgs args = default(BsonSerializationArgs))
{
return ToMyJson(obj, typeof(TNominalType), writerSettings, serializer, configurator, args);
}
/// <summary>
/// Serializes an object to a JSON string.
/// </summary>
/// <param name="obj">The object.</param>
/// <param name="nominalType">The nominal type of the objectt.</param>
/// <param name="writerSettings">The JsonWriter settings.</param>
/// <param name="serializer">The serializer.</param>
/// <param name="configurator">The serialization context configurator.</param>
/// <param name="args">The serialization args.</param>
/// <returns>
/// A JSON string.
/// </returns>
/// <exception cref="System.ArgumentNullException">nominalType</exception>
/// <exception cref="System.ArgumentException">serializer</exception>
public static string ToMyJson(
this object obj,
Type nominalType,
JsonWriterSettings writerSettings = null,
IBsonSerializer serializer = null,
Action<BsonSerializationContext.Builder> configurator = null,
BsonSerializationArgs args = default(BsonSerializationArgs))
{
if (nominalType == null)
{
throw new ArgumentNullException("nominalType");
}
if (serializer == null)
{
serializer = BsonSerializer.LookupSerializer(nominalType);
}
if (serializer.ValueType != nominalType)
{
var message = string.Format("Serializer type {0} value type does not match document types {1}.", serializer.GetType().FullName, nominalType.FullName);
throw new ArgumentException(message, "serializer");
}
using (var stringWriter = new StringWriter())
{
using (var bsonWriter = new JsonWriterMine(stringWriter, writerSettings ?? JsonWriterSettings.Defaults))
{
var context = BsonSerializationContext.CreateRoot(bsonWriter, configurator);
args.NominalType = nominalType;
serializer.Serialize(context, args, obj);
}
return stringWriter.ToString();
}
}
}
}
So basicaly, copy the whole class from github into your own class, and change names of 2 methods: ToJson() into one of yours. Over here you can see that mine are ToJsonMine().
Now, to the second class you will need:
JsonWriter
using MongoDB.Bson.IO;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using MongoDB.Bson;
namespace Fishing.MongoDB.Serializers
{
public class JsonWriterMine : BsonWriter
{
// private fields
private TextWriter _textWriter;
private JsonWriterSettings _jsonWriterSettings; // same value as in base class just declared as derived class
private InternalJsonWriterContext _context;
// constructors
/// <summary>
/// Initializes a new instance of the JsonWriter class.
/// </summary>
/// <param name="writer">A TextWriter.</param>
public JsonWriterMine(TextWriter writer)
: this(writer, JsonWriterSettings.Defaults)
{
}
/// <summary>
/// Initializes a new instance of the JsonWriter class.
/// </summary>
/// <param name="writer">A TextWriter.</param>
/// <param name="settings">Optional JsonWriter settings.</param>
public JsonWriterMine(TextWriter writer, JsonWriterSettings settings)
: base(settings)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
_textWriter = writer;
_jsonWriterSettings = settings; // already frozen by base class
_context = new InternalJsonWriterContext(null, ContextType.TopLevel, "");
State = BsonWriterState.Initial;
}
/// <summary>
/// Writes a BSON DateTime to the writer.
/// </summary>
/// <param name="value">The number of milliseconds since the Unix epoch.</param>
public override void WriteDateTime(long value)
{
if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
{
ThrowInvalidState("WriteDateTime", BsonWriterState.Value, BsonWriterState.Initial);
}
WriteNameHelper(Name);
switch (_jsonWriterSettings.OutputMode)
{
case JsonOutputMode.Strict:
var utcDateTimeFirst = BsonUtils.ToDateTimeFromMillisecondsSinceEpoch(value);
_textWriter.Write($"\"{utcDateTimeFirst.ToString("yyyy-MM-ddTHH:mm:ss.FFFZ")}\"");
break;
case JsonOutputMode.Shell:
default:
// use ISODate for values that fall within .NET's DateTime range, and "new Date" for all others
if (value >= BsonConstants.DateTimeMinValueMillisecondsSinceEpoch &&
value <= BsonConstants.DateTimeMaxValueMillisecondsSinceEpoch)
{
var utcDateTime = BsonUtils.ToDateTimeFromMillisecondsSinceEpoch(value);
_textWriter.Write("ISODate(\"{0}\")", utcDateTime.ToString("yyyy-MM-ddTHH:mm:ss.FFFZ"));
}
else
{
_textWriter.Write("new Date({0})", value);
}
break;
}
State = GetNextState();
}
}
}
This is where the magic happens. Copy the whole class JsonWriter from GitHub into your own and give it a new name(Extend BsonWriter nad implement all methods). Now, here you can manipulate how do you want your date to be serialized. Change WriteDateTime(long value) accordingly. As you can see, in the case JsonOutputMode.Strict: I changed it to return me a DateTime object formatted the way I need it.
And lastly, since MongoSerializer has the internal class that is called JsonWriterContext, you need to create your own and use it in JsonWriter(step 2).
This is how it looks with me(you can copy it whole):
using MongoDB.Bson.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Fishing.MongoDB.Serializers
{
public class InternalJsonWriterContext
{
// private fields
private InternalJsonWriterContext _parentContext;
private ContextType _contextType;
private string _indentation;
private bool _hasElements = false;
// constructors
internal InternalJsonWriterContext(InternalJsonWriterContext parentContext, ContextType contextType, string indentChars)
{
_parentContext = parentContext;
_contextType = contextType;
_indentation = (parentContext == null) ? indentChars : parentContext.Indentation + indentChars;
}
// internal properties
internal InternalJsonWriterContext ParentContext
{
get { return _parentContext; }
}
internal ContextType ContextType
{
get { return _contextType; }
}
internal string Indentation
{
get { return _indentation; }
}
internal bool HasElements
{
get { return _hasElements; }
set { _hasElements = value; }
}
}
}
After you have everything, you will see in the MongoCursorJsonConverter(1st step), in the second ToJsonMine() method that I have this line:
using (var bsonWriter = new JsonWriterMine(stringWriter, writerSettings ?? JsonWriterSettings.Defaults))
You should just replace it with your own custom class that you created in step 2 and it will work like a charm.
And in the end, you just call:
var lists = mongoListBatch.ToJson(settings);
and it will serialize date like you added inside WriteDate(long value)

Example: Mono's DataContractJsonSerializer won't serialize, but .NET will

This is a program that is ready to run in Xamarin and VS2013.
I'm having an issue where mono isn't calling the serializer Subclass, and need to work around that issue.
Question
How should I modify SetMembershipProof so that it will call a method with the attribute [OnSerializing] located in a nested subclass of ?
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading.Tasks;
namespace MonoBug
{
class Program
{
static void Main(string[] args)
{
SetMembershipProof2 setMembershipProof = new SetMembershipProof2();
string setProofJSON = CryptoSerializer.Serialize<SetMembershipProof2>(setMembershipProof);
// Inspect the contents of SetProofJSON, it is null under mono, and not null in .NET
}
}
public class CryptoSerializer
{
/// <summary>
/// Serialize serializable types in namespace UProveCrypto.PolyProof.
/// </summary>
/// <typeparam name="T">input type</typeparam>
/// <param name="obj">instance of serializable type</param>
/// <returns>JSON string</returns>
public static string Serialize<T>(T obj)
{
string result;
try
{
using (MemoryStream ms = new MemoryStream())
{
DataContractJsonSerializer jsonSerializer =
new DataContractJsonSerializer(obj.GetType());
jsonSerializer.WriteObject(ms, obj);
ms.Position = 0;
StreamReader reader = new StreamReader(ms);
result = reader.ReadToEnd();
}
}
catch (Exception e)
{
throw new SerializationException(obj.GetType().Name, e);
}
return result;
}
}
[DataContract]
public abstract class GroupParameterizedSerializer2
{
[OnSerializing]
public void SerializeGroup(StreamingContext context)
{
}
}
[DataContract]
public class SetMembershipProof2 : GroupParameterizedSerializer2
{
#region Serialization
/// <summary>
/// Serialization of a
/// </summary>
[DataMember(Name = "a", EmitDefaultValue = false, Order = 2)]
internal string[] _a;
/// <summary>
/// Serialization of c
/// </summary>
[DataMember(Name = "c", EmitDefaultValue = false, Order = 3)]
internal string[] _c;
/// <summary>
/// Serialization of r
/// </summary>
[DataMember(Name = "r", EmitDefaultValue = false, Order = 4)]
internal string[] _r;
/// <summary>
/// Serialize a, c, r.
/// </summary>
/// <param name="context">The streaming context.</param>
[OnSerializing]
internal void OnSerializing(StreamingContext context)
{
Console.WriteLine("Debug: This isn't called in Mono...");
List<string> t = new List<string>();
t.Add("data1");
_a = t.ToArray();
t.Clear();
t.Add("data2");
_c = t.ToArray();
t.Clear();
t.Add("data3");
_r = t.ToArray();
}
#endregion
}
}
Change your serializer to:
internal string GetJson<T>(T obj)
{
...
DataContractJsonSerializer jsonSerializer =
new DataContractJsonSerializer(obj.GetType());
...
}
Depending on with how you call GetJson<T>, typeof(T) might return the superclass of the serialized object obj. On the other hand, obj.GetType() always returns the specific class of the object that is being serialized. This may be why the [OnSerializing] methods of the subclasses are not being called in Mono.

Synchronising a collection over a websocket connection

I'm working on a client-server system at the moment, and I'm trying to get a collection to synchronise across a websocket. Everything is in C# + .Net 4.5, and I was wondering if there was a particular best practise for synchronising data over a websocket. It's a one way sync:
Server: BindingCollection< MyClass > ----- Websocket -----> Client: BindingCollection< MyClass >
The collection could be up to 1000 objects with 20 fields each so sending the whole lot each time seems a little wasteful.
I would use a observer pattern and only send the changed object to be synced.
So I finally took the time to write a small example.
I am using a in-memory generic repository that invokes events on changes. The changes is then sent to all clients so that you do not have to send the complete list/collection.
A simple model to monitor
using System;
namespace SynchronizingCollection.Common.Model
{
public class MyModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
}
A Generic Repository
Notice the event OnChange that is called when something is added/updated/removed. The event is "subscribed" to in a XSockets long running controller (a singleton) See the "RepoMonitor" class
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace SynchronizingCollection.Server.Repository
{
/// <summary>
/// A static generic thread-safe repository for in-memory storage
/// </summary>
/// <typeparam name="TK">Key Type</typeparam>
/// <typeparam name="T">Value Type</typeparam>
public static class Repository<TK, T>
{
/// <summary>
/// When something changes
/// </summary>
public static event EventHandler<OnChangedArgs<TK,T>> OnChange;
private static ConcurrentDictionary<TK, T> Container { get; set; }
static Repository()
{
Container = new ConcurrentDictionary<TK, T>();
}
/// <summary>
/// Adds or updates the entity T with key TK
/// </summary>
/// <param name="key"></param>
/// <param name="entity"></param>
/// <returns></returns>
public static T AddOrUpdate(TK key, T entity)
{
var obj = Container.AddOrUpdate(key, entity, (s, o) => entity);
if(OnChange != null)
OnChange.Invoke(null,new OnChangedArgs<TK, T>(){Key = key,Value = entity, Operation = Operation.AddUpdate});
return obj;
}
/// <summary>
/// Removes the entity T with key TK
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static bool Remove(TK key)
{
T entity;
var result = Container.TryRemove(key, out entity);
if (result)
{
if (OnChange != null)
OnChange.Invoke(null, new OnChangedArgs<TK, T>() { Key = key, Value = entity, Operation = Operation.Remove});
}
return result;
}
/// <summary>
/// Removes all entities matching the expression f
/// </summary>
/// <param name="f"></param>
/// <returns></returns>
public static int Remove(Func<T, bool> f)
{
return FindWithKeys(f).Count(o => Remove(o.Key));
}
/// <summary>
/// Find all entities T matching the expression f
/// </summary>
/// <param name="f"></param>
/// <returns></returns>
public static IEnumerable<T> Find(Func<T, bool> f)
{
return Container.Values.Where(f);
}
/// <summary>
/// Find all entities T matching the expression f and returns a Dictionary TK,T
/// </summary>
/// <param name="f"></param>
/// <returns></returns>
public static IDictionary<TK, T> FindWithKeys(Func<T, bool> f)
{
var y = from x in Container
where f.Invoke(x.Value)
select x;
return y.ToDictionary(x => x.Key, x => x.Value);
}
/// <summary>
/// Returns all entities as a Dictionary TK,T
/// </summary>
/// <returns></returns>
public static IDictionary<TK, T> GetAllWithKeys()
{
return Container;
}
/// <summary>
/// Returns all entities T from the repository
/// </summary>
/// <returns></returns>
public static IEnumerable<T> GetAll()
{
return Container.Values;
}
/// <summary>
/// Get a single entity T with the key TK
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static T GetById(TK key)
{
return Container.ContainsKey(key) ? Container[key] : default(T);
}
/// <summary>
/// Get a single entity T as a KeyValuePair TK,T with the key TK
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static KeyValuePair<TK, T> GetByIdWithKey(TK key)
{
return Container.ContainsKey(key) ? new KeyValuePair<TK, T>(key, Container[key]) : new KeyValuePair<TK, T>(key, default(T));
}
/// <summary>
/// Checks if the repository has a key TK
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static bool ContainsKey(TK key)
{
return Container.ContainsKey(key);
}
}
}
Event argument and an enum to know what change just happend
using System;
namespace SynchronizingCollection.Server.Repository
{
/// <summary>
/// To send changes in the repo
/// </summary>
/// <typeparam name="TK"></typeparam>
/// <typeparam name="T"></typeparam>
public class OnChangedArgs<TK,T> : EventArgs
{
public Operation Operation { get; set; }
public TK Key { get; set; }
public T Value { get; set; }
}
}
namespace SynchronizingCollection.Server.Repository
{
/// <summary>
/// What kind of change was performed
/// </summary>
public enum Operation
{
AddUpdate,
Remove
}
}
The Controller that send changes to the clients...
using System;
using SynchronizingCollection.Common.Model;
using SynchronizingCollection.Server.Repository;
using XSockets.Core.XSocket;
using XSockets.Core.XSocket.Helpers;
using XSockets.Plugin.Framework;
using XSockets.Plugin.Framework.Attributes;
namespace SynchronizingCollection.Server
{
/// <summary>
/// Long running controller that will send information to clients about the collection changes
/// </summary>
[XSocketMetadata(PluginRange = PluginRange.Internal, PluginAlias = "RepoMonitor")]
public class RepositoryMonitor : XSocketController
{
public RepositoryMonitor()
{
Repository<Guid, MyModel>.OnChange += RepositoryOnChanged;
}
private void RepositoryOnChanged(object sender, OnChangedArgs<Guid, MyModel> e)
{
switch (e.Operation)
{
case Operation.Remove:
this.InvokeTo<Demo>(p => p.SendUpdates, e.Value,"removed");
break;
case Operation.AddUpdate:
this.InvokeTo<Demo>(p => p.SendUpdates, e.Value, "addorupdated");
break;
}
}
}
}
The XSockets controller that clients call to add/remove/update the collection.
using System;
using SynchronizingCollection.Common.Model;
using SynchronizingCollection.Server.Repository;
using XSockets.Core.XSocket;
namespace SynchronizingCollection.Server
{
public class Demo : XSocketController
{
public bool SendUpdates { get; set; }
public Demo()
{
//By default all clients get updates
SendUpdates = true;
}
public void AddOrUpdateModel(MyModel model)
{
Repository<Guid, MyModel>.AddOrUpdate(model.Id, model);
}
public void RemoveModel(MyModel model)
{
Repository<Guid, MyModel>.Remove(model.Id);
}
}
}
And a demo client in C# that adds and removed 10 different objects... But it would be easy to use the JavaScript API as well. Especially with knockoutjs for manipulating the collection on the client.
using System;
using System.Threading;
using SynchronizingCollection.Common.Model;
using XSockets.Client40;
namespace SynchronizingCollection.Client
{
class Program
{
static void Main(string[] args)
{
var c = new XSocketClient("ws://127.0.0.1:4502","http://localhost","demo");
c.Controller("demo").OnOpen += (sender, connectArgs) => Console.WriteLine("Demo OPEN");
c.Controller("demo").On<MyModel>("addorupdated", model => Console.WriteLine("Updated " + model.Name));
c.Controller("demo").On<MyModel>("removed", model => Console.WriteLine("Removed " + model.Name));
c.Open();
for (var i = 0; i < 10; i++)
{
var m = new MyModel() {Id = Guid.NewGuid(), Name = "Person Nr" + i, Age = i};
c.Controller("demo").Invoke("AddOrUpdateModel", m);
Thread.Sleep(2000);
c.Controller("demo").Invoke("RemoveModel", m);
Thread.Sleep(2000);
}
Console.ReadLine();
}
}
}
You can download the project from my dropbox: https://www.dropbox.com/s/5ljbedovx6ufkww/SynchronizingCollection.zip?dl=0
Regards
Uffe

How to serialize/deserialize Object

I'm writting a Wp8/C# library for querying REST Api of MongoLab.
I have an abtract object like this :
[DataContract]
public abstract class Entity
{
[DataMember(Name = "_id")]
public string _id { get; set; }
}
The field _id is autogenerate by Mongo as a ObjectId. But with WP8, I don't have the mongoDb C# driver... The serialization and deserialization doesn't work....
This is what I've tried :
var str = url;
var response = await _httpClient.GetAsync(str);
var rep = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(rep);
I've tried with Datacontractjsonserializer too.
How can I do that?
Thank you
Here is a class I wrote for dealing with JSON serialization and deserialization in .NET 3.5
Don't forget to add a reference to System.ServiceModel.Web.dll
You can use JsonTools.ObjectToJsonString(rep);
using System;
using System.Text;
using System.Runtime.Serialization.Json;
using System.IO;
namespace Utilities
{
/// <summary>
/// Group of static methods for dealing with JSON.
/// </summary>
public static class JsonTools
{
/// <summary>
/// Serializes an object to JSON string.
/// </summary>
/// <param name="obj">The object to serialize. </param>
/// <returns></returns>
/// <exception cref="System.Runtime.Serialization.InvalidDataContractException"></exception>
/// <exception cref="System.Runtime.Serialization.SerializationException"></exception>
/// <exception cref="System.ServiceModel.QuotaExceededExceptionn"></exception>
public static string ObjectToJsonString(object obj)
{
try
{
MemoryStream jsonStream = new MemoryStream();
DataContractJsonSerializer js = new DataContractJsonSerializer(obj.GetType());
js.WriteObject(jsonStream, obj);
jsonStream.Position = 0;
StreamReader sr = new StreamReader(jsonStream);
return sr.ReadToEnd();
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Serializes an object to JSON byte array.
/// </summary>
/// <param name="obj">The object to serialize. </param>
/// <returns></returns>
/// <exception cref="System.Runtime.Serialization.InvalidDataContractException"></exception>
/// <exception cref="System.Runtime.Serialization.SerializationException"></exception>
/// <exception cref="System.ServiceModel.QuotaExceededExceptionn"></exception>
public static byte[] ObjectToJsonByteArray(object obj)
{
try
{
MemoryStream jsonStream = new MemoryStream();
DataContractJsonSerializer js = new DataContractJsonSerializer(obj.GetType());
js.WriteObject(jsonStream, obj);
jsonStream.Position = 0;
return jsonStream.ToArray();
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Deserializes a JSON formatted string to an object of the defined type
/// </summary>
/// <param name="jsonString">JSON formatted string</param>
/// <param name="objType">The type of the object which the jsonString is to be Deserialized to.</param>
/// <returns>Deserialized object</returns>
/// <exception cref="System.Runtime.Serialization.SerializationException"></exception>
public static object JsonStringToObject(string jsonString, Type objType)
{
try
{
DataContractJsonSerializer js = new DataContractJsonSerializer(objType);
byte[] jsonBytes = Encoding.Default.GetBytes(jsonString);
MemoryStream jsonStream = new MemoryStream(jsonBytes);
return js.ReadObject(jsonStream);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Deserializes a JSON formatted byte array to an object of the defined type
/// </summary>
/// <param name="jsonBytes">JSON formatted byte array</param>
/// <param name="objType">The type of the object which the jsonString is to be Deserialized to.</param>
/// <returns>Deserialized object</returns>
/// <exception cref="System.Runtime.Serialization.SerializationException"></exception>
public static object JsonByteArrayToObject(byte[] jsonBytes, Type objType)
{
try
{
DataContractJsonSerializer js = new DataContractJsonSerializer(objType);
MemoryStream jsonStream = new MemoryStream(jsonBytes);
return js.ReadObject(jsonStream);
}
catch (Exception)
{
throw;
}
}
}
}

Categories