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

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.

Related

I need an alternative similar to System.Runtime.Remoting that is supported in .NET 5.0

I have a current software product written in C# WinForms that I'm migrating to WPF .NET 5.0. I've almost got it all migrated, but one of the last projects to migrate is the remoting support for the app. This is intended for remote control from a client in an intranet, NOT Internet.
The current version of the product uses .NET Remoting, however, I sadly have found that this is not supported in .NET 5.0.
I've seen something about gRPC, but can't find anything to show me how to implement or whether there might be something more similar to .NET Remoting to use so that there are minimal changes required.
Thanks in advance for your help!
#Fildor, this isn't really an answer, perse, just posting what I ended up doing for a simplistic approach, rather than leveraging a remoting framework. My remoting implementation always runs on a private network or over a corporate VPN, so security isn't an issue. Thus, I can take a simplistic approach.
What I finally ended up implementing is a simple TCP/IP Socket implementation using TcpClient class and TcpListener that passes Json strings to represent a command and the response objects defined as classes. This class includes server API methods, each of which map to the defined API.
I created a class that inherits from TcpClient (RemoteClientSocket) for the client and another that inherits from TcpListener (RemoteListener) on the server.This class includes client API methods, each of which map to defined API.
I defined an enum, CommandType , that includes types for each remote command.
The RemoteClientSocket class includes method, SendCommand() that is invoked by each of the client API methods after instantiating a RemoteClientCommand object, this method takes a RemoteClientCommand argument.
The RemoteListener class includes methods for each of the remote commands defined in the CommandType enum.
I then created RemoteClientCommand class that is serialized into a Json string and sent from the client and deserialized by the RemoteListener object, which maps to the server command. I then created a RemoteClientResponse class that is serialized to a Json string, which is returned to the client. Each class includes a Serialize() and DeSerialize() method to serialize/deserialze to/from a Json string that is actually sent over the TCP/IP connection.
Lastly, add methods to the RemoteClientSocket class for each of the commands that populates the CommandArgs object array to send to the server and add methods to the RemoteListener class that that executes the server method corresponding to the CommandType contained in the RemoteClientCommand object.
After the RemoteListener executes the command, it instantiates a new RemoteCommandResponse object and populates the CommandReturn object and the CommandOutParams object array, calls Serialize() on the RemoteCommandResponse object and sends that Json string in response over the TCP/IP connection.
When the RemoteClientSocket receives the response, it calls DeSerialize() on the Json response string and unpackages the CommandReturn object, if any, and the CommandOutParams, if any, before returning to from the client method.
Here is the code for the RemoteListener:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using RemoteClient;
namespace RemoteServer
{
public class RemoteListener : TcpListener, IWinCalRemotingService
{
private static readonly IPAddress _ipAddress = IPAddress.Parse("127.0.0.1"); // Local host
// Keep track of the threads and create CancellationTokenSource for each
private Dictionary<Thread, CancellationTokenSource> m_ThreadDictionary = new Dictionary<Thread, CancellationTokenSource>();
public RemoteListener()
: base(_ipAddress, Constants.DEFAULT_REMOTING_PORT)
{
RegisterCommands();
}
/// <summary>
/// Start the server
/// </summary>
public void StartServer()
{
Start();
// Create a thread for the server to listen on
Thread t = new Thread(new ThreadStart(StartListener));
t.Start();
}
/// <summary>
/// Stop the server
/// </summary>
public void StopServer()
{
foreach (KeyValuePair<Thread, CancellationTokenSource> pair in m_ThreadDictionary)
{
// Cancel all of the client threads
pair.Value.Cancel();
}
Stop();
}
public void StartListener()
{
try
{
while (true)
{
Debug.WriteLine("Waiting for a Remoting Connection...");
TcpClient client = AcceptTcpClient();
Debug.WriteLine($"Remoting Client Connected from IP Address:{client.Client.RemoteEndPoint}");
Thread t = new Thread(new ParameterizedThreadStart(HandleClient));
// Add a mapping
m_ThreadDictionary.Add(t, new CancellationTokenSource());
t.Start(client);
}
}
catch (SocketException e)
{
Debug.WriteLine("SocketException: {0}", e);
}
}
public void HandleClient(Object obj)
{
TcpClient client = (TcpClient)obj;
CancellationTokenSource cancelToken = m_ThreadDictionary[Thread.CurrentThread];
var stream = client.GetStream();
string imei = String.Empty;
string remoteCommand = null;
Byte[] bytes = new Byte[512];
int i;
try
{
while (!cancelToken.IsCancellationRequested && (i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
string hex = BitConverter.ToString(bytes);
remoteCommand = Encoding.ASCII.GetString(bytes, 0, i);
Debug.WriteLine("{1}: Received: {0}", remoteCommand, Thread.CurrentThread.ManagedThreadId);
string response = ProcessCommand(remoteCommand);
Byte[] reply = System.Text.Encoding.ASCII.GetBytes(response);
stream.Write(reply, 0, reply.Length);
Debug.WriteLine("{1}: Sent: {0}", response, Thread.CurrentThread.ManagedThreadId);
}
m_ThreadDictionary[Thread.CurrentThread].Dispose();
}
catch (Exception e)
{
SystemEventsMgr.AddException("Remoting Server Client Thread Exception", e);
}
finally
{
// Remove this thread from the map
m_ThreadDictionary.Remove(Thread.CurrentThread);
client.Close();
}
}
private string ProcessCommand(string jsonCommand)
{
RemoteClientCommand cmd = RemoteClientCommand.DeSerialize(jsonCommand);
RemoteCommandResponse response = null;
switch (cmd.CommandType)
{
case ClientCommand.GetAutoInfo:
GetAutoInfo(cmd);
break;
}
return response.Serialize();
}
}
private RemoteCommandResponse GetAutoInfo(RemoteClientCommand cmd)
{
int autoID = cmd.CommandArgs[0];
string manufacturer = "";
string model = "";
int year = 0;
string retString = GetAutoInfo(autoID, out string manufacturer, out string model, out int year);
object[] outParams = new object[]{manufacturer, model, year};
RemoteCommandResponse resp = new RemoteCommandResponse(CommandType.GetAutoInfo, retString, outParams);
return resp;
}
private string GetAutoInfo(int autoID, out string autoManufacturer, out string autoModel, out int autoYear)
{
autoManufacturer = _autos[autoID].Manufacturer;
autoModel = _autos[autoID].Model;
autoYear = _autos[autoID].Year;
return "Success";
}
}
Here is the code for the RemoteClientSocket:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace RemoteClient
{
/// <summary>
/// IMPORTANT: When adding new commands, do the following:
/// 1. Add to the command types
/// 2. Add to the RemoteListener ProcessCommands switch statement
/// </summary>
public enum ClientCommand
{
GetAutoInfo,
}
class RemoteClientSocket : TcpClient, IAutoInfoInterface
{
/// <summary>
/// Instantiate and connect the client
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
public RemoteClientSocket(string server, int port)
: base(server, port)
{
}
/// <summary>
/// Send a command of input type and receive a response as output type
/// </summary>
/// <param name="cmdObject">The Command Object</param>
/// <param name="inputType">The Type for the Command Object</param>
/// <param name="outputType">the Type for the return object</param>
/// <returns></returns>
public RemoteCommandResponse SendCommand(RemoteClientCommand cmd)
{
RemoteCommandResponse cmdResp = null;
NetworkStream stream = GetStream();
Byte[] data = Encoding.ASCII.GetBytes(cmd.Serialize());
stream.Write(data, 0, data.Length);
data = new Byte[256];
string response = string.Empty;
Int32 bytes = stream.Read(data, 0, data.Length);
response = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
// Deserialize the response
cmdResp = RemoteCommandResponse.DeSerialize(response);
stream.Close();
return cmdResp;
}
virtual public string GetAutoInfo(int autoID, out string autoManufacturer, out string autoModel, out int autoYear)
{
string err = "None";
RemoteClientCommand cmd = new RemoteClientCommand(CommandType.GetAutoInfo, new object[] { autoID });
RemoteCommandResponse resp = SendCommand(cmd);
// Unpackage the return type and output arguments
err = resp.CommandReturn as string;
autoManufacturer = resp.CommandOutParams[0] as string;
autoModel = resp.CommandOutParams[1] as string;
autoYear = (int)resp.CommandOutParams[2];
return err;
}
}
/// <summary>
/// The WinCal Remoting Client Command Package Containing Json Strings for Objects
/// </summary>
public class RemoteClientCommand
{
/// <summary>
/// Command Type - one for each remote command
/// </summary>
public WinCalClientCommand CommandType { get; private set; }
/// <summary>
/// Command argument object(s)
/// </summary>
public object[] CommandArgs { get; private set; }
public RemoteClientCommand(WinCalClientCommand cmdType, object[] cmdArgs)
{
CommandType = cmdType;
CommandArgs = cmdArgs;
}
/// <summary>
/// Serialize the class to Json string for the command
/// </summary>
/// <returns></returns>
public string Serialize()
{
return JsonSerializer.Serialize(this);
}
/// <summary>
/// Deserialize the command object
/// </summary>
/// <param name="jsonString"></param>
/// <returns></returns>
public static RemoteClientCommand DeSerialize(string jsonString)
{
return JsonSerializer.Deserialize(jsonString, typeof(RemoteCommandResponse)) as RemoteClientCommand;
}
}
/// <summary>
/// The object serialized and returned by the RemoteListener
/// </summary>
public class RemoteCommandResponse
{
/// <summary>
/// The Command Type - one for each remote command
/// </summary>
public WinCalClientCommand CommandType { get; private set; }
/// <summary>
/// The single command return object
/// </summary>
public object CommandReturn { get; private set; }
/// <summary>
/// The Json strings for all out params - may be empty
/// </summary>
public object[] CommandOutParams { get; private set; }
public RemoteCommandResponse(WinCalClientCommand cmdType, object retObj, object[] outParamObjs)
{
CommandType = cmdType;
CommandReturn = retObj;
CommandOutParams = outParamObjs;
}
/// <summary>
/// Serialize the class to a Json string
/// </summary>
/// <returns></returns>
public string Serialize()
{
return JsonSerializer.Serialize(this);
}
/// <summary>
/// Deserialize the response object
/// </summary>
/// <param name="jsonString"></param>
/// <returns></returns>
public static RemoteCommandResponse DeSerialize(string jsonString)
{
return JsonSerializer.Deserialize(jsonString,typeof(RemoteCommandResponse)) as RemoteCommandResponse;
}
}
}

How to add extension points in my library?

I've started writing DataFilters a while ago after I discovered and used elastic search with it's wonderful lucene syntax.
The idea behind that project was first to learn new stuff but I was also wondering if I could create something similar to work with other datasources.
Long story short, I now have something that work pretty well (I think) with the BCL classes and I now want to extend it to support third party libraries like NodaTime.
The main parts are IFilter interface
using System;
namespace DataFilters
{
/// <summary>
/// Defines the basic shape of a filter
/// </summary>
public interface IFilter : IEquatable<IFilter>
{
/// <summary>
/// Gets the JSON representation of the filter
/// </summary>
/// <returns></returns>
string ToJson();
/// <summary>
/// Computes a new <see cref="IFilter"/> instance which is the exact opposite of the current instance.
/// </summary>
/// <returns>The exact opposite of the current instance.</returns>
IFilter Negate();
#if NETSTANDARD2_1
public virtual void ToString() => ToJson();
#endif
}
}
with two implementations :
Filter
using DataFilters.Converters;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Schema;
using static Newtonsoft.Json.DefaultValueHandling;
using static Newtonsoft.Json.Required;
using System.Text.RegularExpressions;
#if !NETSTANDARD1_3
using System.Text.Json.Serialization;
#endif
namespace DataFilters
{
/// <summary>
/// An instance of this class holds a filter
/// </summary>
#if NETSTANDARD1_3
[JsonObject]
[JsonConverter(typeof(FilterConverter))]
#else
[System.Text.Json.Serialization.JsonConverter(typeof(FilterConverter))]
#endif
public class Filter : IFilter, IEquatable<Filter>
{
/// <summary>
/// Filter that always returns <c>true</c>
/// </summary>
public static Filter True => new Filter(default, default);
/// <summary>
/// Pattern that field name should respect.
/// </summary>
/// <returns></returns>
public const string ValidFieldNamePattern = #"[a-zA-Z_]+((\[""[a-zA-Z0-9_]+""]|(\.[a-zA-Z0-9_]+))*)";
/// <summary>
/// Regular expression used to validate
/// </summary>
/// <returns></returns>
public static readonly Regex ValidFieldNameRegex = new Regex(ValidFieldNamePattern, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1));
/// <summary>
/// Name of the json property that holds the field name
/// </summary>
public const string FieldJsonPropertyName = "field";
/// <summary>
/// Name of the json property that holds the operator
/// </summary>
public const string OperatorJsonPropertyName = "op";
/// <summary>
/// Name of the json property that holds the value
/// </summary>
public const string ValueJsonPropertyName = "value";
/// <summary>
/// <see cref="FilterOperator"/>s that required <see cref="Value"/> to be null.
/// </summary>
public static IEnumerable<FilterOperator> UnaryOperators { get; } = new[]{
FilterOperator.IsEmpty,
FilterOperator.IsNotEmpty,
FilterOperator.IsNotNull,
FilterOperator.IsNull
};
/// <summary>
/// Generates the <see cref="JSchema"/> for the specified <see cref="FilterOperator"/>.
/// </summary>
/// <param name="op"></param>
/// <returns></returns>
public static JSchema Schema(FilterOperator op)
{
JSchema schema;
switch (op)
{
case FilterOperator.Contains:
case FilterOperator.StartsWith:
case FilterOperator.EndsWith:
schema = new JSchema
{
Type = JSchemaType.Object,
Properties =
{
[FieldJsonPropertyName] = new JSchema { Type = JSchemaType.String },
[OperatorJsonPropertyName] = new JSchema { Type = JSchemaType.String },
[ValueJsonPropertyName] = new JSchema { Type = JSchemaType.String }
},
Required = { FieldJsonPropertyName, OperatorJsonPropertyName }
};
break;
case FilterOperator.IsEmpty:
case FilterOperator.IsNotEmpty:
case FilterOperator.IsNotNull:
case FilterOperator.IsNull:
schema = new JSchema
{
Type = JSchemaType.Object,
Properties =
{
[FieldJsonPropertyName] = new JSchema { Type = JSchemaType.String },
[OperatorJsonPropertyName] = new JSchema { Type = JSchemaType.String }
},
Required = { FieldJsonPropertyName, OperatorJsonPropertyName }
};
break;
default:
schema = new JSchema
{
Type = JSchemaType.Object,
Properties =
{
[FieldJsonPropertyName] = new JSchema { Type = JSchemaType.String, },
[OperatorJsonPropertyName] = new JSchema { Type = JSchemaType.String },
[ValueJsonPropertyName] = new JSchema {
Not = new JSchema() { Type = JSchemaType.Null }
}
},
Required = { FieldJsonPropertyName, OperatorJsonPropertyName, ValueJsonPropertyName }
};
break;
}
schema.AllowAdditionalProperties = false;
return schema;
}
/// <summary>
/// Name of the field the filter will be applied to
/// </summary>
#if NETSTANDARD1_3
[JsonProperty(FieldJsonPropertyName, Required = Always)]
#else
[JsonPropertyName(FieldJsonPropertyName)]
#endif
public string Field { get; }
/// <summary>
/// Operator to apply to the filter
/// </summary>
#if NETSTANDARD1_3
[JsonProperty(OperatorJsonPropertyName, Required = Always)]
[JsonConverter(typeof(CamelCaseEnumTypeConverter))]
#else
[JsonPropertyName(OperatorJsonPropertyName)]
//[System.Text.Json.Serialization.JsonConverter(typeof(FilterOperatorConverter))]
#endif
public FilterOperator Operator { get; }
/// <summary>
/// Value of the filter
/// </summary>
#if NETSTANDARD1_3
[JsonProperty(ValueJsonPropertyName,
Required = AllowNull,
DefaultValueHandling = IgnoreAndPopulate,
NullValueHandling = NullValueHandling.Ignore)]
#else
[JsonPropertyName(ValueJsonPropertyName)]
#endif
public object Value { get; }
/// <summary>
/// Builds a new <see cref="Filter"/> instance.
/// </summary>
/// <param name="field">name of the field</param>
/// <param name="operator"><see cref="Filter"/> to apply</param>
/// <param name="value">value of the filter</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="field"/> does not conform with <see cref="ValidFieldNamePattern"/></exception>
public Filter(string field, FilterOperator #operator, object value = null)
{
if (!string.IsNullOrEmpty(field) && !ValidFieldNameRegex.IsMatch(field))
{
throw new ArgumentOutOfRangeException(nameof(field), field, $"field name is not valid ({ValidFieldNamePattern}).");
}
Field = field;
switch (#operator)
{
case FilterOperator.EqualTo when value is null:
Operator = FilterOperator.IsNull;
break;
case FilterOperator.NotEqualTo when value is null:
Operator = FilterOperator.IsNotNull;
break;
default:
Operator = #operator;
Value = value;
break;
}
}
#if NETSTANDARD1_3
public string ToJson()
{
return this.Jsonify(new JsonSerializerSettings());
}
#else
public string ToJson() => this.Jsonify();
#endif
public override string ToString() => ToJson();
public bool Equals(Filter other)
=> other != null
&& (ReferenceEquals(other, this)
|| (Equals(other.Field, Field) && Equals(other.Operator, Operator) && Equals(other.Value, Value)));
public override bool Equals(object obj) => Equals(obj as Filter);
#if NETSTANDARD1_3 || NETSTANDARD2_0
public override int GetHashCode() => (Field, Operator, Value).GetHashCode();
#else
public override int GetHashCode() => HashCode.Combine(Field, Operator, Value);
#endif
public IFilter Negate()
{
FilterOperator #operator = Operator switch
{
FilterOperator.EqualTo => FilterOperator.NotEqualTo,
FilterOperator.NotEqualTo => FilterOperator.EqualTo,
FilterOperator.IsNull => FilterOperator.IsNotNull,
FilterOperator.IsNotNull => FilterOperator.IsNull,
FilterOperator.LessThan => FilterOperator.GreaterThan,
FilterOperator.GreaterThan => FilterOperator.LessThan,
FilterOperator.GreaterThanOrEqual => FilterOperator.LessThanOrEqualTo,
FilterOperator.StartsWith => FilterOperator.NotStartsWith,
FilterOperator.NotStartsWith => FilterOperator.StartsWith,
FilterOperator.EndsWith => FilterOperator.NotEndsWith,
FilterOperator.NotEndsWith => FilterOperator.EndsWith,
FilterOperator.Contains => FilterOperator.NotContains,
FilterOperator.IsEmpty => FilterOperator.IsNotEmpty,
FilterOperator.IsNotEmpty => FilterOperator.IsEmpty,
FilterOperator.LessThanOrEqualTo => FilterOperator.GreaterThanOrEqual,
_ => throw new ArgumentOutOfRangeException(nameof(Operator), "Unknown operator"),
};
return new Filter(Field, #operator, Value);
}
public bool Equals(IFilter other) => Equals(other as Filter)
;
public void Deconstruct(out string field, out FilterOperator #operator, out object value)
{
field = Field;
#operator = Operator;
value = Value;
}
}
}
MultiFilter
using DataFilters.Converters;
using Newtonsoft.Json;
using Newtonsoft.Json.Schema;
using System;
using System.Collections.Generic;
using System.Linq;
using static Newtonsoft.Json.DefaultValueHandling;
using static Newtonsoft.Json.Required;
#if !NETSTANDARD1_3
using System.Text.Json.Serialization;
#endif
namespace DataFilters
{
/// <summary>
/// An instance of this class holds combination of <see cref="IFilter"/>
/// </summary>
[JsonObject]
#if NETSTANDARD1_3
[JsonConverter(typeof(MultiFilterConverter))]
#else
[System.Text.Json.Serialization.JsonConverter(typeof(MultiFilterConverter))]
#endif
public class MultiFilter : IFilter, IEquatable<MultiFilter>
{
/// <summary>
/// Name of the json property that holds filter's filters collection.
/// </summary>
public const string FiltersJsonPropertyName = "filters";
/// <summary>
/// Name of the json property that holds the composite filter's logic
/// </summary>
public const string LogicJsonPropertyName = "logic";
public static JSchema Schema => new JSchema
{
Type = JSchemaType.Object,
Properties =
{
[FiltersJsonPropertyName] = new JSchema { Type = JSchemaType.Array, MinimumItems = 2 },
[LogicJsonPropertyName] = new JSchema { Type = JSchemaType.String, Default = "and"}
},
Required = { FiltersJsonPropertyName },
AllowAdditionalProperties = false
};
/// <summary>
/// Collections of filters
/// </summary>
#if NETSTANDARD1_3
[JsonProperty(PropertyName = FiltersJsonPropertyName, Required = Always)]
#else
[JsonPropertyName(FiltersJsonPropertyName)]
#endif
public IEnumerable<IFilter> Filters { get; set; } = Enumerable.Empty<IFilter>();
/// <summary>
/// Operator to apply between <see cref="Filters"/>
/// </summary>
#if NETSTANDARD1_3
[JsonProperty(PropertyName = LogicJsonPropertyName, DefaultValueHandling = IgnoreAndPopulate)]
[JsonConverter(typeof(CamelCaseEnumTypeConverter))]
#else
[JsonPropertyName(LogicJsonPropertyName)]
#endif
public FilterLogic Logic { get; set; }
public virtual string ToJson() => this.Jsonify();
public IFilter Negate()
{
MultiFilter filter = new MultiFilter
{
Logic = Logic switch
{
FilterLogic.And => FilterLogic.Or,
FilterLogic.Or => FilterLogic.And,
_ => throw new ArgumentOutOfRangeException($"Unsupported {Logic}")
},
Filters = Filters.Select(f => f.Negate())
#if DEBUG
.ToArray()
#endif
};
return filter;
}
#if NETSTANDARD1_3 || NETSTANDARD2_0
public override int GetHashCode() => (Logic, Filters).GetHashCode();
#else
public override int GetHashCode()
{
HashCode hash = new HashCode();
hash.Add(Logic);
foreach (IFilter filter in Filters)
{
hash.Add(filter);
}
return hash.ToHashCode();
}
#endif
public bool Equals(IFilter other) => Equals(other as MultiFilter);
public override bool Equals(object obj) => Equals(obj as MultiFilter);
public bool Equals(MultiFilter other)
=> Logic == other?.Logic
&& Filters.Count() == other?.Filters?.Count()
&& Filters.All(filter => other?.Filters?.Contains(filter) ?? false)
&& (other?.Filters.All(filter => Filters.Contains(filter)) ?? false);
}
}
DataFilters.Expressions and DataFilters.Queries are two libraries that I also wrote and that allow to create C# Expressions or WHERE SQLs given an IFilter instance as a input (extension methods).
What i'm trying to do now is to provide an extension point so that I could write a new library (called DataFilters.NodaTime for example) that could handle NodaTime types somehow while deferring everything else to DataFilters.Expressions (a library that I already released).
That extension point should add ability to handle nodatime type but I have no clue how to get started on this
For now, I'm thinking about something like this :
create a new library DataFilters.Expressions.NodaTime
create a new IFilter extension method in it : it will be tailored to handle NodaTime types.
The goal is to be able to handle NodaTime types both with DataFilters.Expressions and DataFilters.Queries for example.
Could it be a good approach or is there a better way to handle this ?
Thanks in advance to anyone who could help me on this

Best way to unit test classes using streams?

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!

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)

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