1

We have objects which rely on both the ISerializable (to mark the interfaces as serializable for implementers) and internally there are also JsonObject and JsonProperty attributes. As an example:

[JsonObject("Example")]
[Serializable]
public class Example : ISerializable
{
    [JsonProperty("TEST")]
    string _testString;

    public string TestString
    {
        get => _testString;
        set => _testString = value;
    }

    protected Example(SerializationInfo info, StreamingContext context)
    {
        if (info == null)
        {
            throw new ArgumentNullException(nameof(info), $"{nameof(info)} is null.");
        }

        _testString = info.GetString(nameof(TestString));
    }

    protected virtual void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue(nameof(TestString), _testString);
    }

    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (info == null)
        {
            throw new ArgumentNullException(nameof(info), $"{nameof(info)} is null.");
        }

        GetObjectData(info, context);
    }
}

So is there a way to enforce the Json serializer to use either the ISerializable interface or the attributes. If there is no option to enforce, perhaps there is the option to prioritize the different mechanisms?

Update

Based on one comment below I added a contract resolver but this leads to wrong/missing content in my serialized string - I guess I use the contract resolver in a wrong way?

public class ContractResolver : IContractResolver
{
    public JsonContract ResolveContract(Type type)
    {
        if(typeof(ISerializable).IsAssignableFrom(type))
        {
            return new JsonISerializableContract(type);
        }
        return new JsonObjectContract(type);
    }
}
Franz Gsell
  • 1,425
  • 2
  • 13
  • 22
  • Not exactly clear how `ISerializable` (assuming https://stackoverflow.com/questions/810974/what-is-the-point-of-the-iserializable-interface) is related to text serialization.... – Alexei Levenkov Mar 10 '21 at 08:55
  • It is not related to text serialization. It just means the object can be serialized - and I think that's also the reason why this interface was supported in JSON.NET. So I do not really understand your comment? – Franz Gsell Mar 10 '21 at 09:59
  • Why do you need both ways? Do you want to serialize the object differently based on the chosen option? – Peter Csala Mar 10 '21 at 11:52
  • @PeterCsala: Exactly this is what I want to achieve. – Franz Gsell Mar 10 '21 at 14:21
  • 1
    I think you need to implement a custom Contract Resolver and override the `CreateContract` method. In case of `ISerializable` you need to use `JsonISerializableContract` otherwise `JsonObjectContract`. – Peter Csala Mar 10 '21 at 15:19
  • @PeterCsala: I think this is the right direction. I updated my question above with a sample implementation but now some of my data is missing in the serialized text but unwanted parts appear. I think I misunderstood the usage of "CreateContract"? – Franz Gsell Mar 10 '21 at 16:19
  • if you simply want to disable use of `ISerializable` across the board set [`DefaultContractResolver.IgnoreSerializableInterface = true`](https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_Serialization_DefaultContractResolver_IgnoreSerializableInterface.htm) as shown in [JSON.NET is ignoring properties in types derived from System.Exception. Why?](https://stackoverflow.com/a/27197432/3744182). Is that enough, or do you need to selectively ignore `ISerializable` only for specific types? – dbc Mar 10 '21 at 16:43

1 Answers1

2

If you want to create custom logic you would need to create your IContractResolver by deriving it from DefaultContractResolver. Then override the protected CreateContract method instead of ResolveContract.

For example, the following resolver will prioritize ISerializable before the [JsonObject] attribute (it is the other way around in the base implementation).

public class CustomContractResolver : DefaultContractResolver
{
    protected override JsonContract CreateContract(Type type)
    {
        JsonContract contract = base.CreateContract(type);
        if (!IgnoreSerializableInterface &&
            typeof(ISerializable).IsAssignableFrom(type) && 
            !(contract is JsonDictionaryContract))
        {
            return CreateISerializableContract(type);
        }
        return contract;
    }
}
Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
  • It seems this is almost working. Anyway for dictionaries the output is different with the customcontractresolver compared to the default. With the customcontractresolver there is also output like " "Version": 2, "Comparer": { "$type": "System.Collections.Generic.GenericEqualityComparer`1[[System.String, mscorlib]], mscorlib" }, "HashSize": 3," – Franz Gsell Mar 11 '21 at 06:24
  • Another small comment. Instead of calling "return new JsonISerializableContract(type);" it is better to call the "return CreateISerializableContract(type);" Otherwise it is not possible to deserialize the content. – Franz Gsell Mar 11 '21 at 15:23
  • Yes, you're right-- that was an oversight. – Brian Rogers Mar 11 '21 at 15:38
  • I have updated my answer with a fix for the dictionary issue as well. – Brian Rogers Mar 11 '21 at 16:11