Popular blog tags

This blog describer How MongoDb ObjectID JSON String Format in Asp .NET Core。

Issue Description

Here are the Language class. ObjectId is MongoDB.Bson.ObjectId

using System.Collections.Generic;
using Nop.Core.Domain.Stores;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

namespace Nop.Core.Domain.Localization
{
    /// <summary>
    /// Represents a language
    /// </summary>
    [BsonIgnoreExtraElements]
    public partial class Language : BaseEntity, IStoreMappingSupported
    {
        public Language()
        {
            Stores = new List<ObjectId>();
        }

        /// <summary>
        /// Gets or sets the name
        /// </summary>
        public string Name { get; set; } = "English";

        /// <summary>
        /// Gets or sets the language culture
        /// </summary>
        public string LanguageCulture { get; set; } = "en-US";

        /// <summary>
        /// Gets or sets the unique SEO code
        /// </summary>
        public string UniqueSeoCode { get; set; } = "en";

        /// <summary>
        /// Gets or sets the flag image file name
        /// </summary>
        public string FlagImageFileName { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether the language supports "Right-to-left"
        /// </summary>
        public bool Rtl { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores
        /// </summary>
        public bool LimitedToStores { get; set; }
        public IList<ObjectId> Stores { get; set; }

        /// <summary>
        /// Gets or sets the identifier of the default currency for this language; 0 is set when we use the default currency display order
        /// </summary>
        public ObjectId DefaultCurrencyId { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether the language is published
        /// </summary>
        public bool Published { get; set; } = true;

        /// <summary>
        /// Gets or sets the display order
        /// </summary>
        public int DisplayOrder { get; set; } = 1;

    }
}

data in mongodb 

{
	"_id" : ObjectId("5cf6b5c32bac284b687fa506"),
	"GenericAttributes" : [ ],
	"Name" : "zh",
	"LanguageCulture" : "zh",
	"UniqueSeoCode" : "zh",
	"FlagImageFileName" : "zh.png",
	"Rtl" : false,
	"LimitedToStores" : false,
	"Stores" : [ ],
	"DefaultCurrencyId" : ObjectId("5cf6b5c32bac284b687fa506"),
	"Published" : true,
	"DisplayOrder" : 1
}

Serialization/Deserialization in .NET Core or ASP.NET Core 5.x gives below or similar error:

[2021-02-26T13:06:17.019Z] Information: 
WebSocket connected to ws://localhost:51403/_blazor?id=CGMq7Bhkt1jmrG8lQtFnXA.
blazor.server.js:19 [2021-02-26T13:06:29.416Z] Error: System.Text.Json.JsonException: 
The JSON value could not be converted to MongoDB.Bson.ObjectId.

 

Add JsonObjectIdConverter convert ObjectId to string,otherwise it is “00000000000000000000000000”

with Nopcommerce admin web Problem I have a collection of dynamic data. I want to get it back like this:

{ _id: "58b454f20960a1788ef48ebb" ... }

Attempts Here are a list of approaches that do not work:

This await resources = _database.GetCollection<BsonDocument>("resources") .Find(Builders<BsonDocument>.Filter.Empty) .ToListAsync();

return Ok(resources);

Yields [[{"name":"_id","value":{"bsonType":7,"timestamp":1488213234,"machine":614561,"pid":30862,"increment":16027323,"creationTime":"2017-02-27T16:33:54Z","rawValue":{"timestamp":1488213234,"machine":614561,"pid":30862,"increment":16027323,"creationTime":"2017-02-27T16:33:54Z"},"value":{"timestamp":1488213234,"machine":614561,"pid":30862,"increment":16027323,"creationTime":"2017-02-27T16:33:54Z"}}}]]

solution

Create Custom ObjectId converter for System.Text.Json

.Net5.x

using System.Text.json solution

 System.Text.Json now supports custom type converters in .NET 3.0 and above.

The new System.Text.Json api exposes a JsonConverter api which allows us to convert the type as we like.

Namespace:System.Text.Json.Serialization
public abstract class JsonConverter<T> : System.Text.Json.Serialization.JsonConverter

we can create a ObjectId to string converter.

office doc:How to write custom converters for JSON serialization

https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-5-0

Resolution 1

step 1:Write ObjectId converter,Converts an object or value to or from JSON.

 

We start by implementing the Read and Write method. The Read method will be used for deserialization and the Write method is used when serializing.

Converts an object or value to or from JSON.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using MongoDB.Bson;

using System.Text.Json;

using System.Text.Json.Serialization;  //JsonConverter<ObjectId>
namespace Nop.Web.Framework.Json
{
    /// <summary>
    /// come from:https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-5-0
    /// </summary>
    public class JsonObjectIdConverterBySystemTextJson:JsonConverter<ObjectId>
    {

        public override ObjectId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => new ObjectId(JsonSerializer.Deserialize<string>(ref reader, options));

        // public override void Write(Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options) => writer.WriteNumberValue(value.Id);

        public override void Write(Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options)
        {
            writer.WriteStringValue(value.ToString());
        }
    }
}

Note: The above converter converts from ObjectId to String or convert string id to ObjectIds.

You'll still need to  register the ObjectIdConverter globally

 

 web api project

step 2:Register a custom converter in web api project

Globally use a JsonConverter on a class without the attribute.

 Register it in System.Text.json globally with global settings and you not need mark you models with big attributes.

When working with MVC/Razor Page, we can register this converter in startup.cs:

  public void ConfigureServices(IServiceCollection services)
        {

            services.AddControllers()
              .AddJsonOptions(options =>
               {
                   options.JsonSerializerOptions.PropertyNamingPolicy = null;
                   options.JsonSerializerOptions.Converters.Add(new JsonObjectIdConverterBySystemTextJson());
               }
                );

Test

 

http://localhost:5000/api/EmailAccount

 // GET: api/<controller>
        [HttpGet]
        public async  Task<ActionResult<IList<EmailAccount>>> Get()
        {
            var emailAccount = _emailAccountService.GetAllEmailAccounts();

          

            return Ok(emailAccount);
        }

output

[
{"Email":"[email protected]",
"DisplayName":"Store name",
"Host":"smtp.mail.com",
"Port":2500,"Username":"123",
"Password":"123",
"EnableSsl":false,
"UseDefaultCredentials":false,
"FriendlyName":"[email protected] (Store name)",
"GenericAttributes":[],
"Id":"5cf6a0da501c97205c1d1da6"
},
{"Email":"[email protected]","DisplayName":"ASP.NET Core Blazor","Host":"","Port":25,"Username":"","Password":"","EnableSsl":false,"UseDefaultCredentials":false,"FriendlyName":"[email protected] (ASP.NET Core Blazor)","GenericAttributes":[],
"Id":"5e9267e739323e2eb4162bb4"
}
]

Making application level changes for JSON serialization

1. Globally configuring values for JSON Serializer in ASP.NET Core 5.x

A few code changes in Startup class is as follows:

public void ConfigureServices(IServiceCollection services)
{
    ...

   services.AddControllersWithViews().AddJsonOptions(options =>
       {
      options.JsonSerializerOptions.Converters.Add(new FloatConverter());
    });
}

2. Globally configuring values for JSON Serializer in ASP.NET Core Web Api 5.x

  public void ConfigureServices(IServiceCollection services)
        {
            //come from:https://docs.microsoft.com/en-us/aspnet/core/web-api/advanced/formatting?view=aspnetcore-5.0
            services.AddControllers()
              .AddJsonOptions(options =>
               {
                   // Use the default property (Pascal) casing.
                   options.JsonSerializerOptions.PropertyNamingPolicy = null;
                   // Configure a custom converter.
                   options.JsonSerializerOptions.Converters.Add(new JsonObjectIdConverterUsingSystemTextJson());
               }
                );

 

3.Blazor Server project

step 1: use httpclient get json string from web api

 

step 2: 

 

step 3:

 

step 4:How to globally set default options for System.Text.Json.JsonSerializer?

 

 

solution 2

Create Custom ObjectId converter for Newtonsoft.Json

using Newtonsoft.Json

Step 1:Write a JsonObjectIdConverter,it will convert ObjectId to string when object field type is ObjectId

using MongoDB.Bson;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;

namespace Nop.Web.Framework.Json
{

    /// <summary>
    /// question and answer:https://www.cpume.com/question/ffzgitse-mongo-c-sharp-driver-and-objectid-json-string-format-in-net-core.html
    /// soucecode come from :https://gist.github.com/cleydson/d1583f87f6fb7e2a8ee67e2455a1bb56
    /// </summary>
    public class JsonObjectIdConverter : JsonConverter
    {
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            if (value.GetType().IsArray)
            {
                writer.WriteStartArray();
                foreach (var item in (Array)value)
                {
                    serializer.Serialize(writer, item);
                }
                writer.WriteEndArray();
            }
            else
                serializer.Serialize(writer, value.ToString());
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var token = JToken.Load(reader);
            var objectIds = new List<ObjectId>();

            if (token.Type == JTokenType.Array)
            {
                foreach (var item in token.ToObject<string[]>())
                {
                    objectIds.Add(new ObjectId(item));
                }
                return objectIds.ToArray();
            }

            if (token.ToObject<string>().Equals("MongoDB.Bson.ObjectId[]"))
            {
                return objectIds.ToArray();
            }
            else
                return new ObjectId(token.ToObject<string>());
        }

        public override bool CanConvert(Type objectType)
        {
            return (objectType == typeof(ObjectId));
        }
    }
}

 

How to use:

using Newtonsoft.Json;
using MongoDB.Bson;

using Nop.Web.Framework.Json;

namespace Nop.Web.Framework.Mvc.Models
{
    /// <summary>
    /// Base nopCommerce model
    /// </summary>
    /// 

    public partial class BaseNopModel
    {
        [JsonConverter(typeof(JsonObjectIdConverter))]
        public ObjectId Id { get; set; }
	}

    /// <summary>
    /// Base nopCommerce entity model
    /// </summary>
    public partial class BaseNopEntityModel : BaseNopModel
    {
        
    }
}

How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects?
https://stackoverflow.com/questions/16651776/json-net-cast-error-when-serializing-mongo-objectid/37966098#37966098
 

 

objectid regex(Regex for MongoDB ObjectID)

var myregexp = /^[0-9a-fA-F]{24}$/;
subject = "112345679065574883030833";

if (subject.match(myregexp)) {
    // Successful match
} else {
    // Match attempt failed
}

 

Useful links:

office doc:How to write custom converters for JSON serialization

https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-5-0

Create Custom Int32Converter for System.Text.Json

https://www.thecodebuzz.com/create-custom-int32converter-for-system-text-json/

Create a StringConverter for System.Text.Json for JSON Serialization

https://www.thecodebuzz.com/system-text-json-create-a-stringconverter-json-serialization/

Custom Dictionary<string, object> JsonConverter for System.Text.Json

https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/

Create a PersonConverter for System.Text.Json for JSON Serialization

https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-5-0#converter-samples-for-common-scenarios

https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.Formatters.Json/2.0.0 https://www.nuget.org/packages/Microsoft.AspNetCore.JsonPatch/2.0.0  .