Newtonsoft.json: JsonConvert.DeserializeObject does not recognise nested objects in dictionary; return JObject

Created on 20 Dec 2017  路  2Comments  路  Source: JamesNK/Newtonsoft.Json

I have JWT object with a certain payload claim. The claim has value a container of a list of permissions. Serialization is working fine but deserialization is not working. That container is unwrapped but not in correct type, it stays Newtonsoft.Json.Linq.JObject. I use .NET Core 2.0

Source/destination types

namespace JsonConvertXUnitTestProject
{
    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Xunit;

    public class Permission
    {
        public string Name { get; set; }
    }

    public class Container
    {
        public List<Permission> Permissions { get; set; }
    }

    public class Jwt : IEquatable<Jwt>
    {
        public Jwt()
        {
            Header = new Dictionary<string, object>();
            Payload = new Dictionary<string, object>();
        }

        public Dictionary<string, object> Header { get; set; }

        public Dictionary<string, object> Payload { get; set; }

        public string Serialize()
        {
            Dictionary<string, object> dict = new Dictionary<string, object>
            {
                ["header"] = JsonConvert.SerializeObject(Header),
                ["payload"] = JsonConvert.SerializeObject(Payload)
            };
            return JsonConvert.SerializeObject(dict);
        }

        public void Deserialize(string deserialized)
        {
            Dictionary<string, object> dict = JsonConvert.DeserializeObject<Dictionary<string, object>>(deserialized);
            Header = JsonConvert.DeserializeObject<Dictionary<string, object>>(dict["header"].ToString());
            Payload = JsonConvert.DeserializeObject<Dictionary<string, object>>(dict["payload"].ToString());
        }

        public override bool Equals(object obj)
        {
            return Equals(obj as Jwt);
        }

        public static bool DictEquals(Dictionary<string, object> dict1, Dictionary<string, object> dict2)
        {
            if (dict1 == null && dict2 == null)
            {
                return true;
            }
            else if (dict1 != null && dict2 != null)
            {
                if (dict1 == dict2) // Reference comparison (if both points to same object)
                {
                    return true;
                }
                else if (dict1.Count == dict2.Count)
                {
                    return dict1.Except(dict2).Count() == 0;
                }
            }
            return false;
        }

        public bool Equals(Jwt other)
        {
            return other != null &&
                   DictEquals(Header, other.Header) &&
                   DictEquals(Payload, other.Payload);
        }

        public override int GetHashCode()
        {
            var hashCode = 1268427973;
            hashCode = hashCode * -1521134295 + EqualityComparer<Dictionary<string, object>>.Default.GetHashCode(Header);
            hashCode = hashCode * -1521134295 + EqualityComparer<Dictionary<string, object>>.Default.GetHashCode(Payload);
            return hashCode;
        }

        public static bool operator ==(Jwt jwt1, Jwt jwt2)
        {
            return EqualityComparer<Jwt>.Default.Equals(jwt1, jwt2);
        }

        public static bool operator !=(Jwt jwt1, Jwt jwt2)
        {
            return !(jwt1 == jwt2);
        }
    }

    public class ObjectJsonConvertUnitTest
    {
        [Fact]
        public void TestObject()
        {
            Container cont = new Container
            {
                Permissions = new List<Permission>
                {
                    new Permission() { Name = "open" },
                    new Permission() { Name = "close" }
                }
            };
            Jwt t1 = new Jwt();
            t1.Header["alg"] = "None";
            t1.Header["kid"] = "1";
            t1.Payload["sub"] = "Object";
            t1.Payload["perm"] = cont;
            Jwt t2 = new Jwt();
            string serialized = t1.Serialize();
            t2.Deserialize(serialized);
            Assert.Equal(t1, t2);
        }
        [Fact]
        public void TestObjectStrait()
        {
            Container cont = new Container
            {
                Permissions = new List<Permission>
                {
                    new Permission() { Name = "open" },
                    new Permission() { Name = "close" }
                }
            };
            Jwt t1 = new Jwt();
            t1.Header["alg"] = "None";
            t1.Header["kid"] = "1";
            t1.Payload["sub"] = "Object";
            t1.Payload["perm"] = cont;
            Jwt t2 = new Jwt();
            string serialized = JsonConvert.SerializeObject(t1);
            t2 = JsonConvert.DeserializeObject<Jwt>(serialized);
            Assert.Equal(t1, t2);
        }        
    }
}
csproj package reference item group is here:
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0-preview-20170628-02" />
    <PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
    <PackageReference Include="xunit" Version="2.2.0" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />

Source/destination JSON

{"Header":{"alg":"None","kid":"1"},"Payload":{"sub":"Object","perm":{"Permissions":[{"Name":"open"},{"Name":"close"}]}}}

{
  "Header": {
    "alg": "None",
    "kid": "1"
  },
  "Payload": {
    "sub": "Object",
    "perm": {
      "Permissions": [{
          "Name": "open"
        },{
          "Name": "close"
        }
      ]
    }
  }
}

Expected behavior

Expected behavior would be equality of objects input and output. The output get from input after application of serialization and deserialization consequently.

Actual behavior

Output object is different from input. Container with permissions object is not fully deserialized, it is returned in a JObject instance.

Steps to reproduce

            Jwt t1 = new Jwt();
            ...
            string serialized = JsonConvert.SerializeObject(t1);
            t2 = JsonConvert.DeserializeObject<Jwt>(serialized);
            Assert.Equal(t1, t2);

Most helpful comment

Json.NET doesn't know what the serialized type is. All it has is JSON, so it defaults to JValue

All 2 comments

I have the very same behavior with .NET Framework 4.6.1 too.

Json.NET doesn't know what the serialized type is. All it has is JSON, so it defaults to JValue

Was this page helpful?
0 / 5 - 0 ratings