0

I want to design an API that accept some generic contract and also can store additional values that extends generic schema. It looks like IExtensibleDataObject machinery was built for similar task and it can be useful for my usecase.

But, when I try to use it, I found strange behaviour of the ExtensionDataObject property deserialization in case of usage of EmitDefaultValue = false option for DataMember.

Consider following data contracts (note, that fields of the Item DTO has EmitDefaultValue = false option):

[DataContract(Name = "Contract", Namespace = "")]
public class ContractGeneric : IExtensibleDataObject
{
    [DataMember(Name = "id", Order = 1)]
    public string Id;

    public ExtensionDataObject? ExtensionData { get; set; }
}

[DataContract(Name = "Contract", Namespace = "")]
public class ContractExtended
{
    [DataMember(Name = "id", Order = 1)]
    public string Id;

    [DataMember(Name = "items", Order = 2)]
    public Item[] Items;
}

[DataContract(Name = "Item", Namespace = "")]
public class Item
{
    [DataMember(Name = "code", Order = 1, EmitDefaultValue = false)]
    public string Code;

    [DataMember(Name = "name", Order = 2, EmitDefaultValue = false)]
    public string Name;
}

The definition of contracts looks fine for me, but serialization of ContractGeneric DTO parsed from some ContractExtended DTO with DataContractJsonSerializer can crash with a strange exception:

Unhandled exception. System.Runtime.Serialization.SerializationException: There was an error serializing the object of type TestSerialization.ContractGeneric. Encountered unexpected el
ement local name 'code' for item in collection. 'item' is the only valid local name for elements in a collection.

For example, you can get a crash situation with the following example:

public class Program
{
    public static void Main()
    {
        var v2 = new ContractExtended {Id = "1", Items = new[] {new Item {Code = "0"}}};
        var v2Json = ToJson(v2);
        Console.WriteLine(v2Json);
        var v1 = FromJson<ContractGeneric>(v2Json);
        var v1Json = ToJson(v1);
        Console.WriteLine(v1Json);
    }
    
    public static string ToJson<T>(T value)
    {
        using var memoryStream = new MemoryStream();
        new DataContractJsonSerializer(typeof(T)).WriteObject(memoryStream, value);
        return Encoding.UTF8.GetString(memoryStream.ToArray());
    }

    public static T FromJson<T>(string json)
    {
        using var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json));
        return (T) new DataContractJsonSerializer(typeof(T)).ReadObject(memoryStream);
    }
}

It looks like there is a bug in the deserialization process and internally ExtendedObject think that one element of items collection also is a collection, but this is wrong and serializator detect this discrepancy only at the time of serialization process.

Can I avoid such strange exception while preserving EmitDefaultValue option and also stay in the JSON world (because it seems like XML serialization handles this situations correctly). Or this is a bug or some kind of limitation of DataContractJsonSerializer?

dbc
  • 104,963
  • 20
  • 228
  • 340
Nikita Sivukhin
  • 2,370
  • 3
  • 16
  • 33
  • There is an error in serializing (not deserializing). Are you getting data from a database and then sending the data? XML serialization requires predefined classes. JSON does not require redefined classes. Looks like you want to send JSON but getting an xml error. – jdweng Jun 14 '21 at 19:40
  • @jdweng I'm getting the data from database in the JSON format, but I don't think that it is very important to my question. At the moment I'm stuck with the code snippet from the question ([full combined source code](https://gist.github.com/Umqra/a9cef9f3ec0e53cd9ed1843af82404a8)). I see an error and can't understand - is it my fault in the configuration of serializator/data contract or is it a bug in the serializator. – Nikita Sivukhin Jun 14 '21 at 19:48
  • You need to figure out if the issue is the the results from the database of the serializing of the results. You may be getting null from the database and then failing when you try to serialize a null object. XML serialize is harder to get working because you need to put the data into classes. JSON will serialize an object without classes. – jdweng Jun 14 '21 at 19:58
  • @jdweng The question contains the minimal reproducible example for the problem. Example code simply create one extended object, serialize it to the string, then deserialize generic contract and finally serialize it to the string again. This example reproduce my problem and the crash occurs at the final stage. Now I want to understand is it some known bug and how I can mitigate it (if it is). – Nikita Sivukhin Jun 14 '21 at 20:07
  • Order should start with zero not one. – jdweng Jun 14 '21 at 23:15
  • @jdweng I tried to use zero-based numeration for DataMembers, but still got the same exception for my code snippet. – Nikita Sivukhin Jun 15 '21 at 06:31

0 Answers0