C
C#13mo ago
Owez

Implementing JsonConverter to simplify ser/de

I've been trying to implemement a JsonConverter for a simple class to simplify the default C# tagging using JsonSerializer. Here's the class I'm trying to convert:
public class Snapshot<T>
{
public DateTime Taken { get; set; }
public T Data { get; set; }

public static Snapshot<T> NewEmpty()
{
return new Snapshot<T>
{
Taken = DateTime.MinValue,
Data = default
};
}
}
public class Snapshot<T>
{
public DateTime Taken { get; set; }
public T Data { get; set; }

public static Snapshot<T> NewEmpty()
{
return new Snapshot<T>
{
Taken = DateTime.MinValue,
Data = default
};
}
}
And here's an example serialized output for what I need:
{"2020-06-21T00:00:00":"this is some data here!!"}
{"2020-06-21T00:00:00":"this is some data here!!"}
(FYI the default serializer outputs {"Taken":"2020-06-21T00:00:00","Data":"this is some data here!!"}). How do I implement a JsonConverter to Read & Write to my desired format? I need the generics to always work and this Snapshot will be used as an attribute of a parent class I've wrote this so far which feels like it's going in the right direction, but I'm not sure if the JsonSerializer line here is right and I'm not sure about how to implement Read:
public class SnapshotConverter<T> : JsonConverter<Snapshot<T>>
{
public override Snapshot<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException("Deserialization is not supported.");
}

public override void Write(Utf8JsonWriter writer, Snapshot<T> value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WritePropertyName(value.Taken.ToString("s"));
JsonSerializer.Serialize(writer, value.Data, options);
writer.WriteEndObject();
}
}
public class SnapshotConverter<T> : JsonConverter<Snapshot<T>>
{
public override Snapshot<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException("Deserialization is not supported.");
}

public override void Write(Utf8JsonWriter writer, Snapshot<T> value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WritePropertyName(value.Taken.ToString("s"));
JsonSerializer.Serialize(writer, value.Data, options);
writer.WriteEndObject();
}
}
30 Replies
ero
ero13mo ago
quite sure this is impossible
Owez
Owez13mo ago
Are there any libraries to do this sort of thing? I'm basically reimplementing my rust client in C#
ero
ero13mo ago
i mean i'll just be real, this is a really bad json layout like, awful
Vi Ness
Vi Ness13mo ago
{"2020-06-21T00:00:00":"this is some data here!!"}
{"2020-06-21T00:00:00":"this is some data here!!"}
Looks like a KeyValuePair entry for a Dictionary<DateTime, T>. Would that be an option instead of using Snapshot<T>?
Owez
Owez13mo ago
God yeah I messed up Thats the json for the Element Haha hang on let me get the actual class Sorry about that
ero
ero13mo ago
dictionary is the only thing i can think of as well but the key of a json entry must be a string so
Owez
Owez13mo ago
I think I was debugging the wrong class for a little while, I'll check if it applies and send the right problem :)
ero
ero13mo ago
the date should definitely never be the key
Vi Ness
Vi Ness13mo ago
This looks like an answer to a similar issue https://stackoverflow.com/a/41503997
Stack Overflow
Serialize List> as JSON
I'm very new with JSON, please help! I am trying to serialise a List<KeyValuePair<string, string>> as JSON Currently: [{"Key":"MyKey 1","Value":"MyValue 1"},{"Key":"MyKey 2","Value":"
Owez
Owez13mo ago
Yeah the JSON is for a class with a dictionary inside (called Elements), posted the wrong bit but I was debugging assuming that JSON which is why I couldn't get it to work Had the wrong test case Yep seems to work This is what I actually needed help with haha:
public class Elements<T>
{
public Dictionary<DateTime, T> Values { get; set; }

public Elements(Dictionary<DateTime, T> values)
{
Values = values;
}

public static Elements<T> NewTest(string isoDateString, T firstValue)
{
DateTime parsedDate = DateTime.Parse(isoDateString);
Dictionary<DateTime, T> values = new Dictionary<DateTime, T>
{
{ parsedDate, firstValue }
};
return new Elements<T>(values);
}
}
public class Elements<T>
{
public Dictionary<DateTime, T> Values { get; set; }

public Elements(Dictionary<DateTime, T> values)
{
Values = values;
}

public static Elements<T> NewTest(string isoDateString, T firstValue)
{
DateTime parsedDate = DateTime.Parse(isoDateString);
Dictionary<DateTime, T> values = new Dictionary<DateTime, T>
{
{ parsedDate, firstValue }
};
return new Elements<T>(values);
}
}
How do I flatten this so there's no {"Values": [data here]} so its only {[data here]} I need it as a class because it's got related methods otherwise it'd just be a Dictionary. In rust this would look like
#[derive(Serialize, Deserialize)]
pub struct Elements<T>(HashMap<DateTime<Utc>, T>)
#[derive(Serialize, Deserialize)]
pub struct Elements<T>(HashMap<DateTime<Utc>, T>)
ero
ero13mo ago
class Elements<T> : Dictionary<DateTime, T>? but again, the datetime being the key is really weird...
Owez
Owez13mo ago
Did not think about that Timeseries data
ero
ero13mo ago
don't know what that means
Owez
Owez13mo ago
title here is Elements<string> for example
Owez
Owez13mo ago
I'm writing a youtube archiver which can snapshot metadata at different places in time
ero
ero13mo ago
this seems really backwards
ACiDCA7
ACiDCA713mo ago
i think with this you just disqualified yourself from getting anymore help, since to me to me it seems like you are scraping data from yt which iirc is against tos
Owez
Owez13mo ago
the only other way to do it is like that original Snapshot class in a list but then you lose the hashtable
ero
ero13mo ago
{
snapshots: [
{
"date": "...",
"title": "...",
// ...
},
// more snapshots
]
}
{
snapshots: [
{
"date": "...",
"title": "...",
// ...
},
// more snapshots
]
}
this would definitely be better but yeah we can't help with scraping
Owez
Owez13mo ago
metadata captures not perfect so sometimes only certain things are either updated or remain unchanged
ero
ero13mo ago
sure
Owez
Owez13mo ago
and Elements means each is generalised
ero
ero13mo ago
what's it matter
Owez
Owez13mo ago
title.current() a lot easier with ISO alphabetically sorted dates
ero
ero13mo ago
snapshots.orderby(date).first().title
Owez
Owez13mo ago
rather than having to linear search through each snapshot and then see if theres a title in there or if its not been captured or not been updated im tracking each update not each time ive possibly captured it
ero
ero13mo ago
also btw currently all your dates are messed up
Owez
Owez13mo ago
yep they should all use the same time
ero
ero13mo ago
cause you fetch datetime.now anew every single time
Owez
Owez13mo ago
any more critiques? i guess not