skip to Main Content

Below class is the recursive dictionary (tree-like structure). The EntityID is just the alias of string which must be accessed via HierarchicalRelationshipsMap.EntityID.

public class HierarchicalRelationshipsMap: Dictionary<HierarchicalRelationshipsMap.EntityID, HierarchicalRelationshipsMap>
{
    public class EntityID
    {
        public static implicit operator EntityID(string value) => new EntityID();
        public static implicit operator string(EntityID entityID) => string.Empty;
    }
}

Instance example:

HierarchicalRelationshipsMap hierarchicalRelationshipsMap = new()
{
  {
    Guid.NewGuid().ToString(),
    new HierarchicalRelationshipsMap
    {
      {Guid.NewGuid().ToString(), new HierarchicalRelationshipsMap() },
      {Guid.NewGuid().ToString(), new HierarchicalRelationshipsMap() }
    }
  }
};

If to serialize it using

Newtonsoft.Json.JsonConvert.SerializeObject(hierarchicalRelationshipsMap, Newtonsoft.Json.Formatting.Indented));

it will produce this result:

{
  "MockDataSource.HierarchicalRelationshipsMap+EntityID": {
    "MockDataSource.HierarchicalRelationshipsMap+EntityID": {},
    "MockDataSource.HierarchicalRelationshipsMap+EntityID": {}
  }
}

while something like this would be expected:

{
  "2ca15c6b-e706-4f1c-b287-1629ed0bac0a": {
     "21333569-4248-49ac-ba0c-d1612a2c42a8": {}
     "fc7ad745-8890-4a68-9431-c8de4801470b": {}
  }
}

How to fix this?

2

Answers


  1. The issue stems from the way JsonConvert interprets EntityID. Since it is a class, Json.NET doesn’t apply the implicit operator and instead serializes the actual type name. To address that just add implementation of ToString() in EntityID and make it return a GUID. You can also implement custom converter for that class and use it instead.

    Login or Signup to reply.
  2. Well, your implicit operators are just creating empty object/string, whatever data is generated (GUIDs) you discard it with the operators.

    To fix that you need to define:

    public class HierarchicalRelationshipsMap : Dictionary<HierarchicalRelationshipsMap.EntityID, HierarchicalRelationshipsMap>
    {
        public class EntityID
        {
            private readonly string _value;
    
            public EntityID(string value)
            {
                _value = value;
            }
    
            public static implicit operator EntityID(string value) => new EntityID(value);
            public static implicit operator string(EntityID entityID) => entityID._value;
        }
    }
    

    Then you would need to define custom JsonConverter, as by default it sees EntityID as object and serializes it as such. We need to "instruct it" to do serialization differently:

    public class HierarchicalRelationshipsMapConverter : JsonConverter<HierarchicalRelationshipsMap>
    {
        public override void WriteJson(JsonWriter writer, HierarchicalRelationshipsMap value, JsonSerializer serializer)
        {
            writer.WriteStartObject();
            foreach (var kvp in value)
            {
                writer.WritePropertyName((string)kvp.Key);
                serializer.Serialize(writer, kvp.Value);
            }
            writer.WriteEndObject();
        }
    
        public override HierarchicalRelationshipsMap ReadJson(JsonReader reader, Type objectType, HierarchicalRelationshipsMap existingValue, bool hasExistingValue, JsonSerializer serializer)
        {
            var map = new HierarchicalRelationshipsMap();
    
            if (reader.TokenType == JsonToken.StartObject)
            {
                reader.Read();
                while (reader.TokenType == JsonToken.PropertyName)
                {
                    // Read the key as a string and convert it to EntityID
                    HierarchicalRelationshipsMap.EntityID entityId = (string)reader.Value;
    
                    // Read the value as a nested HierarchicalRelationshipsMap
                    reader.Read();
                    var nestedMap = serializer.Deserialize<HierarchicalRelationshipsMap>(reader);
    
                    map[entityId] = nestedMap;
                    reader.Read();
                }
            }
    
            return map;
        }
    }
    

    Then following code snippet:

    HierarchicalRelationshipsMap hierarchicalRelationshipsMap = new()
    {
      {
        Guid.NewGuid().ToString(),
        new HierarchicalRelationshipsMap
        {
          {Guid.NewGuid().ToString(), new HierarchicalRelationshipsMap() },
          {Guid.NewGuid().ToString(), new HierarchicalRelationshipsMap() }
        }
      }
    };
    
    // Serialization settings with custom HierarchicalRelationshipsMapConverter
    var settings = new JsonSerializerSettings
    {
        Formatting = Formatting.Indented,
        Converters = new List<JsonConverter> { new HierarchicalRelationshipsMapConverter() }
    };
    
    // Serialize the map to JSON
    string json = JsonConvert.SerializeObject(hierarchicalRelationshipsMap, settings);
    Console.WriteLine(json);
    

    Produces expected output:

    {
      "4cd7a972-29d1-432f-bad3-909de96882b7": {
        "042dfc86-cbfb-4cc7-a9d3-ba0a234e8fc4": {},
        "99c96d01-eafb-4d26-ab7c-e9851b361be3": {}
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search