JamesNK / Newtonsoft.Json

Json.NET is a popular high-performance JSON framework for .NET
https://www.newtonsoft.com/json
MIT License
10.71k stars 3.24k forks source link

How to use Newtonsoft.Json in place of BinaryFormatter in C# .Net? #2886

Open viveknuna opened 1 year ago

viveknuna commented 1 year ago

We have legacy code, We learned that there are some vulnerabilities using BinaryFormatter. So I am trying to use NewtonSoft.Json. My project is already using NewtonSoft.Json package.

I have tried the below code but it gives an exception on the line var deserializedStream = serializer.Deserialize(jsonTextReader);when running.

Unexpected character encountered while parsing value: . Path '', line 1, position 1.

My new Code:

public void LoadFromDisk()
{
    if (!File.Exists(BINARY_FILENAME)) return;

    var serializer = new JsonSerializer();
    using (var stream = File.Open(BINARY_FILENAME, FileMode.Open, FileAccess.Read))
    {
        using (var sr = new StreamReader(stream))
        {
            using (var jsonTextReader = new JsonTextReader(sr))
            {
                var deserializedStream = serializer.Deserialize(jsonTextReader);
                _jobsAck = deserializedStream as ConcurrentDictionary<string, DateTime>;
                if (_jobsAck == null)
                {
                    _jobsAck = new ConcurrentDictionary<string, DateTime>();
                    if (!(deserializedStream is Dictionary<string, DateTime> ackDict)) return;
                    foreach (var pair in ackDict)
                    {
                        _jobsAck.TryAdd(pair.Key, pair.Value);
                    }
                }
            }
        }
    }
}

Old Code:


public void LoadFromDisk()
{
    if (!File.Exists(BINARY_FILENAME)) return;

    var binaryFormatter = new BinaryFormatter();
    using (var stream = File.Open(BINARY_FILENAME, FileMode.Open, FileAccess.Read))
    {
        var deserializedStream = binaryFormatter.Deserialize(stream);
        _jobsAck = deserializedStream as ConcurrentDictionary<string, DateTime>;
        if (_jobsAck == null)
        {
            _jobsAck = new ConcurrentDictionary<string, DateTime>();
            if (!(deserializedStream is Dictionary<string, DateTime> ackDict)) return;
            foreach (var pair in ackDict)
            {
                _jobsAck.TryAdd(pair.Key, pair.Value);
            }
        }
    }

```}
**Target framework**: 4.7.2 (I cannot upgrade .net framework due to some constraints)
**NewtonSoft version:** 12.0.2
CZEMacLeod commented 1 year ago

At a guess from the error message, it looks like the BINARY_FILENAME file is still written with BinaryFormatter. Make sure you delete the file, and replace your SaveToDisk method first so that the written data is serialized in Json format. Unless you use a custom convention of some sort, I don't think it can deserialize directly to a ConcurrentDictionary.

See Serializing Collections

I think your code could be something like:

using Newtonsoft.Json;
using System.Collections.Concurrent;

internal class Program
{
    private ConcurrentDictionary<string, DateTime> _jobsAck = new();

    private const string JSON_FILENAME = "JobsAck.json";

    public ConcurrentDictionary<string, DateTime> JobsAck { get => _jobsAck; set => _jobsAck = value; }

    public void LoadFromDisk()
    {
        if (!File.Exists(JSON_FILENAME)) return;

        var serializer = new JsonSerializer();
        using (var stream = File.Open(JSON_FILENAME, FileMode.Open, FileAccess.Read))
        {
            using (var sr = new StreamReader(stream))
            {
                using (var jsonTextReader = new JsonTextReader(sr))
                {
                    var ackDict = serializer.Deserialize<Dictionary<string, DateTime>>(jsonTextReader);
                    if (ackDict != null)
                    {
                        _jobsAck = new ConcurrentDictionary<string, DateTime>();
                        foreach (var pair in ackDict)
                        {
                            _jobsAck.TryAdd(pair.Key, pair.Value);
                        }
                    }
                }
            }
        }
    }

    public void SaveToDisk()
    {
        var serializer = new JsonSerializer();
        using (var stream = File.Open(JSON_FILENAME, FileMode.Create, FileAccess.Write))
        {
            using (var sw = new StreamWriter(stream))
            {
                using (var jsonTextWriter = new JsonTextWriter(sw)
                {
                    Formatting = Formatting.Indented    // Not needed, and makes the file a little larger, but is much more readable.
                })
                {
                    serializer.Serialize(jsonTextWriter, _jobsAck);
                }
            }
        }
    }

    static void Main(string[] args)
    {
        var prog = new Program();
        prog.LoadFromDisk();
        prog.JobsAck.TryAdd(Guid.NewGuid().ToString(), DateTime.UtcNow);
        prog.SaveToDisk();
    }
}

Hope this helps.