简体   繁体   中英

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:

  1. Bson Extension Methods
  2. JsonWriter
  3. 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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM