I have implemented a custom JsonConverter to deserialize concrete classes when all we know about is the interface that is using them. given this, I have overridden the ReadJson method with the following:
public class MyConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(IMyInterface));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
if (jsonObject["Type"].Value<string>() == "MyType")
return jsonObject.ToObject<MyConcrete>(serializer);
}
}
On the interface I have added the following decoration:
[JsonConverter(typeof(MyConverter))]
public interface IMyInterface { ... }
The issue I seem to be having is that when deserialization is attempted, I keep getting StackOverflow exception, and I think that it is going around this custom converter for each element in the JSON structure.. which there are quite a few(!)
If I remove the decoration of the interface and instead add the custom converter to the call the converter manually... it works fine!
var jsonSerializerSettings = new JsonSerializerSettings
{
Converters = { new MyConverter() }
};
Is there a way I can resolve this without calling the customer converter specifically? I am concerned about unexpected behavior on deserialization of objects that don't need the custom JsonConverter. Thoughts?
Related
To specify which converter Json.Net serializer should use when dealing with a property, we usually use JsonConverterAttribute, which is a nice and neat syntax.
However, because C# doesn't support the use of generic type parameters in Attribute, trouble occurs when the declaring class of property contains generic type parameters. Here's a simple example:
public class FooConverter<T> : JsonConverter<T> {
public override void WriteJson(JsonWriter writer, T value, JsonSerializer serializer) {
//Implementation
}
public override T ReadJson(JsonReader reader, Type objectType, T existingValue, bool hasExistingValue, JsonSerializer serializer) {
//Implementation
}
}
public class Bar<T>{
[JsonConverter(typeof(FooConverter<T>))] //CS0416 'T': an attribute argument cannot use type parameters
public T Value { get; set; }
}
I know I can instantiate a generic converter and pass it with JsonSerializerSettings when calling JsonConvert.DeserializeObject, but that requires the caller to know such implementation detail of the type being deserialized. What I'm writing is a library, I don't want to bother my user to manually add a converter each time he needs to deserialize a object of such type.
Is there any workaround when my custom converter do rely on the type parameter?
You'll need to use a non-generic converter, e.g.
public class FooConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// ...
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// ...
}
public override bool CanRead
{
get { return true; } // or false if you don't need custom read
}
public override bool CanConvert(Type objectType)
{
// ...
}
}
Then you can use [FooConverter] as an attribute. It won't be type-safe but there's no other way to do this because of the unbound nature of generics. However, as long as you return an object of type objectType in the ReadJson method, you won't have any problems.
More information is here.
I'd like to serialize a property from a class into a json property, and when deserializing back, i'd like to read it to a different property.
Cannot figure out how to achieve this in json.net
Minimal example:
Backend converts a specially formatted json property into a numeric timestamp (firebase systemtimestamp).
I want to use the same abstract base class for both serialization and deserialization.
When serializing, i need to generate a "timestamp" json property with special values.
When deserializing, incoming json contains a number in "timestamp" property. I obviously need this to be read into a different class property.
Class:
[JsonObject(MemberSerialization.OptIn)]
public abstract class ItemBase
{
[JsonProperty("timestamp")]
private TimestampModel WriteTimestamp => TimestampModelInstance;
[JsonProperty("timestamp")]
private long ReadTimestamp { get; set; }
public DateTime Timestamp => DateTimeOffset.FromUnixTimeMilliseconds(ReadTimestamp).LocalDateTime;
}
I would need to somehow mark WriteTimestamp as "serialize only", and ReadTimestampas "deserialize only"
If anyone finds this useful, I resolved the issue with a custom JsonConverter.
This way I have only one class property that has the type of the read value (long), that always serializes into TimestampModelInstance (the special firebase structure) regardless of the actual value
Converter:
public class TimestampConverter : JsonConverter
{
public override bool CanRead => false;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(TimestampModel);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JObject.FromObject(TimestampModelInstance).WriteTo(writer);
}
}
In Class:
[JsonProperty("timestamp")]
[JsonConverter(typeof(TimestampConverter))]
private long NumericTimestamp { get; set; }
public DateTime Timestamp =>
DateTimeOffset.FromUnixTimeMilliseconds(NumericTimestamp).LocalDateTime;
I want to decorate my classes with custom attributes, and read them when I convert to json using json.net inside a custom JsonConverter. I'll then vary the serialization depending on this custom attribute.
public class MyCustomJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
//I want to get any attributes set on the property here.
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Int64);
}
}
Another approach would be to specify my custom JsonConverter on the property using an attribute, but I don't want to do this because I want to inject some behaviour into the constructor of my custom JsonConverter by instantiating the converters in the JsonSerializer settings as below.
String json = JsonConvert.SerializeObject(new MyCLass(), new JsonSerializerSettings
{
Converters = new List
{
new MyCustomJsonConverter()
}
});
I can get to the name of the property in the textWriter path. And I can see some interesting hints in the documentation about Metadata, but I can't find a way to do this.
Here's an example decorated class:
public class MyCustomAttribute : Attribute { }
public class MyCLass
{
[MyCustom]
public Int64 MyInt { get; set; }
}
JsonConverters apply to types, not to fields or properties.
Instead of adding an attribute to a property that uses an existing type, consider creating a new type and writing a convert for that instead.
public struct MyCustomType
{
...
}
public class MyClass
{
public MyCustomType {get; set;}
}
Besides - in what other way would you ever want to serialize a raw integer? If the integer represents something, then create a struct or class for that something.
See also: "ValueObject" (Domain Driven Design fundamental concept)
Based on your comment below, an alternate approach would be to forget about JsonConverters and simply expose a secondary property:
public class MyClass
{
[JsonIgnore]
public Int64 MyInt {get; set;}
[JsonProperty("MyInt")]
public Int64 MyEncryptedInt
{
get { return Encrypt(MyInt); }
set { MyInt = Decrypt(value); }
}
}
I've looked at various questions but I am unsure of how to implement this.
I have a custom struct, which currently has no public properties on it. When it is returned via WebApi (not doing any fancy serialization, just returning the custom struct itself), it is returned as an object {}.
public struct CustomStruct
{
private string myProperty;
...
public override string ToString()
{
return this.myProperty;
}
...
}
The custom struct itself is the type of a property on a parent class, which serializes to:
{ "MyProp1":"value1","MyProp2":"value2","MyCustomStruct":{} }
When I override ToString() on the custom struct I want to output one of the private properties. Can I achieve a similar behaviour when returning the object to JavaScript-land, as a JSON object?
E.g. my private property is a string, called "myProperty", set to "test".
If I added a public property called "MyProperty", I'd get the following output:
{ "MyProp1":"value1","MyProp2":"value2","MyCustomStruct":{ "MyProperty":"test" } }
When what I really want is:
{ "MyProp1":"value1","MyProp2":"value2","MyCustomStruct":"test" }
Hope this makes sense.
Here are the related questions that haven't really helped me much. Would like to avoid using JSON.NET if possible but will go for that if it is the only way:
JSON.Net Struct Serialization Discrepancy
C# custom json serialization
JSON.NET with Custom Serializer to a custom object
JSON serialization of enum as string
JavaScriptSerializer.Deserialize - how to change field names
I faced the same challenge. I solved it by writing a custom JsonConverter that uses the objects ToString method when converting:
public class ToStringConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return true;
}
}
And then using it like this as JsonConverter attribute:
[JsonConverter(typeof(ToStringConverter))]
public AnotherObjectWithToString MyObject { get; set; }
I should note that this can only be used for serialization as deserialization would require the ToString result to converted back into a object and that will vary by type.
What I have done for now, is add a second property on the parent CustomClass class...
public string MyCustomStructValue { get { return MyCustomStruct.ToString(); } }
then add the [IgnoreDataMember] attribute to the original property...
[IgnoreDataMember]
public CustomStruct MyCustomStruct { get; set; }
which works fine with the following action:
public IEnumerable<CustomClass> Get()
{
return GetResults();
}
I'm using a ConcreteCollectionTypeConverter to deserialize objects of type IList. I'm probably doing it very wrong because I keep getting the exception "Cannot convert List to IList"
My object inteface looks something like this:
[JsonObject(MemberSerialization.OptIn)]
public IMyInterface {
[JsonProperty("associatedContact")]
[JsonConverter(typeof(ConcreteCollectionTypeConverter<IList<ISomeOtherInterface>, List<SomeOtherInterface>>))]
IList<ISomeOtherInterface> MyObject { get; set; }
}
My object implementation looks like this:
public MyImplementation : IMyInterface {
public List<SomeOtherImplementation> MyObject { get; set; }
public MyImplementations () {
MyObject = new List<SomeOtherImplementation>();
}
}
My Json Converter looks like this:
class ConcreteCollectionTypeConverter<TInterface, TImplementation> : JsonConverter where TImplementation : TInterface
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return serializer.Deserialize<TImplementation>(reader);
}
}
I'm calling the deserialization method somewhere else in my project like this:
var myDeserializedObject = JsonConvert.DeserializeObject<MyImplementation>(jsonObject.ToString());
My jsonObject mentioned above is correctly parsed.
I know this is possible because I've seen a comment here on SO from a user caliming he's done it (Casting interfaces for deserialization in JSON.NET - first comment of the accepted answer).
This approach works fine for non-collection items (i.e. deserializing an object of an interface type IIterface MyObject with JsonConverer(typeof(ConcreteTypeCOnverter)) works).
If I use only Lists instead of ILists, I don't even need the Type Converter, but my requirements are to assure usage of ICollections because of possible non standard implementations.
I believe this will work for what you need:
public class ConcreteListTypeConverter<TInterface, TImplementation> : JsonConverter where TImplementation : TInterface
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var res = serializer.Deserialize<List<TImplementation>>(reader);
return res.ConvertAll(x => (TInterface) x);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}