You cannot deserialize the JSON object shown in your question to a Dictionary<string, EntityProperty> even with a custom JsonConverter. This is because EntityProperty is a Class for storing information about a single property in an entity in a table. As such, it can capture a variety of .Net primitives including:
byte []
bool
DateTime.
double, int and long.
string
However, your root JSON object's values included nested arrays ("RawSampleIDData") and objects ("Eest"). As such, it cannot be deserialized to a dictionary of EntityProperty primitives. You will need to adopt a different data model to deserialize that JSON.
Relatedly, see:
If your object values were entirely JSON primitives, you could introduce the following converter:
public class EntityPropertyConverter : JsonConverter<EntityProperty>
{
public override void WriteJson(JsonWriter writer, EntityProperty value, JsonSerializer serializer) =>
serializer.Serialize(writer, value.PropertyAsObject);
public override EntityProperty ReadJson(JsonReader reader, Type objectType, EntityProperty existingValue, bool hasExistingValue, JsonSerializer serializer)
{
switch (reader.MoveToContentAndAssert().TokenType)
{
case JsonToken.Null:
return null;
case JsonToken.Integer:
if (reader.Value is int i)
return new EntityProperty(i);
else if (reader.Value is long l)
return new EntityProperty(l);
// BigInteger not supported.
break;
case JsonToken.Float:
if (reader.Value is double d)
return new EntityProperty(d);
else if (reader.Value is decimal m)
return new EntityProperty((double)m);
break;
case JsonToken.String:
return new EntityProperty((string)reader.Value);
case JsonToken.Boolean:
return new EntityProperty((bool)reader.Value);
case JsonToken.Date:
if (reader.Value is DateTime dt)
return new EntityProperty(dt);
else if (reader.Value is DateTimeOffset dto)
return new EntityProperty(dto);
break;
case JsonToken.Bytes:
if (reader.Value is byte [] a)
return new EntityProperty(a);
else if (reader.Value is Guid g)
return new EntityProperty(g);
break;
}
throw new JsonSerializationException(string.Format("Cannot convert value {0} to {1}", reader.TokenType, nameof(EntityProperty)));
}
}
public static partial class JsonExtensions
{
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None) // Skip past beginning of stream.
reader.ReadAndAssert();
while (reader.TokenType == JsonToken.Comment) // Skip past comments.
reader.ReadAndAssert();
return reader;
}
public static JsonReader ReadAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (!reader.Read())
throw new JsonReaderException("Unexpected end of JSON stream.");
return reader;
}
}
And then do:
var settings = new JsonSerializerSettings
{
Converters = { new EntityPropertyConverter() },
};
var dict = data.ToObject<Dictionary<string, EntityProperty>>(JsonSerializer.CreateDefault(settings));
Demo fiddle here.
Update
With the above converter, one option to deserialize an arbitrary JObject to a Dictionary<string, EntityProperty> would be to flatten the object to a dictionary keyed by the path of each primitive values, like so:
var settings = new JsonSerializerSettings
{
Converters = { new EntityPropertyConverter() },
};
var serializer = JsonSerializer.CreateDefault(settings);
var dict = data.Descendants().OfType<JValue>().ToDictionary(v => v.Path, v => v.ToObject<EntityProperty>(serializer));
For the JSON in the question the result is
{
"Version": "123",
"Eest.Bulls": 1848,
"Eest.Message": "Passed (0.325 mA < 2[0] => 'P')",
"Eest.Credit": 0.325,
"Eest.Read": 0.14,
"Eest.SBin": "P",
"HostVersion": "Test",
"RawSampleIDData[0]": 1,
"RawSampleIDData[1]": 3,
"RawSampleIDData[2]": 2,
"RawSampleIDData[3]": 4
}
Demo fiddle #2 here.