0

I'm sending this structure through webapi:

[DataContract]
public class PacketData
{
    public enum Opcodes
    {
        Hello = 0x00,
        Close = 0x01,
        Serial = 0x02,
        GPIO = 0x04
    }

    [DataMember]
    public object Data { get; private set; }
    [DataMember]
    public Opcodes Opcode { get; private set; }

    public PacketData(Opcodes opcode, object data)
    {
        Data = data;
        Opcode = opcode;
    }
}

And my problem is that I set on server side when I sending it I assign to Data few class ex. CustomClass1, CustomClass2

Now on deserialize I get instead of object string which is:

{\r\n  \"Cmd\": 5,\r\n  \"BaudRates\": [\r\n    2400,\r\n    4800,\r\n    9600,\r\n    19200,\r\n    38400,\r\n    57600,\r\n    115200\r\n  ],\r\n  \"SerialPorts\": null,\r\n  \"IsOpen\": false,\r\n  \"BaudRate\": 0,\r\n  \"PortName\": null,\r\n  \"WriteCMD\": null,\r\n  \"WriteDATA\": null,\r\n  \"Read\": null\r\n}

So Data is string instead of class or C# classic object type And there is problem I don't know how to recognize from string if its CustomClass1 or CustomClass2.

Any ideas how to resolve this?

Thanks.

EDIT: Including deserialize and serialize

[HttpGet("Send", Name = "Send")]
public IActionResult Send()
{
    return Ok(WebAPI.Send(), HttpStatusCode.OK);
}

    public IEnumerable<string> Send()
    {
        List<string> packets = new List<string>();

        foreach (PacketData packet in StaticConfig.SendStack.ToArray())
            packets.Add(JsonConvert.SerializeObject(packet));

        return packets.ToArray();
    }

And this is deserialize:

                string json = await client.GetStringAsync(new Uri("http://localhost:5000/api/Send"));

                string[] jsonArray = JsonConvert.DeserializeObject<string[]>(json);

                if (jsonArray.Length == 0)
                    Task.Delay(100).Wait();

                List<PacketData> packets = new List<PacketData>();

                foreach (string j in jsonArray)
                    packets.Add(JsonConvert.DeserializeObject<PacketData>(j));

                foreach (PacketData packet in packets)
                {
                    string p = packet.Data.ToString();

                    bool a = packet.Data is PacketSerialModel; // returns false

                    HandleReceivedData(this, new ReceivedDataArgs(packet));
                }

EDIT 2: So what do I want?

I would like to get back mentioned string into PacketData.Data then I can use something like this:

if(packet.Data is CustomClass1)
{
}
else if(packet.Data is CustomClass2)
{
  var b = packetData as CustomClass2;
  //...
}

currently my packet.Data is string and I need to create on this object properties and set values to them based on json.

EDIT3: using now

JsonSerializerSettings()
{ TypeNameHandling = TypeNameHandling.Auto }

Works perfectly, but I have to replace in incoming json string project name like in following string:

["{\"Data\":{\"$type\":\"Shared.PacketSerialModel, ASP_MVC\",\"Cmd\":5,\"BaudRates\":[2400,4800,9600,19200,38400,57600,115200],\"SerialPorts\":null,\"IsOpen\":false,\"BaudRate\":0,\"PortName\":null,\"WriteCMD\":null,\"WriteDATA\":null,\"Read\":null},\"Opcode\":2}"]

I have to replace ASP_MVC to second project name, any workaround than replace?

user1085907
  • 1,009
  • 2
  • 16
  • 40
  • 2
    What serializer are you using? You are using `[DataContract]` attributes but mention `JsonConverter` in your post title. Can you share the code you are using to deserialize? – dbc Feb 10 '16 at 20:57
  • Something's missing here. Are you doing a JsonConvert and then assigning the result to the Data field before sending it over the wire? That could be why you're getting a string on the other side. – villecoder Feb 10 '16 at 21:00
  • Okay I edited post, I forgot that, sorry :( my bad – user1085907 Feb 10 '16 at 21:12
  • I am confused as to what your question is. Would you mind please rewording the question? – A.sharif Feb 10 '16 at 21:48
  • So the problem is that you want `Data` property to be deserialized to correct type but still be an object? – Aleksandr Ivanov Feb 10 '16 at 22:02
  • Yes I would like to be it still object, then I can determine in simple condition if its class1 or class2 or class3, .. – user1085907 Feb 10 '16 at 22:22
  • You could serialize and deserialize with [`TypeNameHandling.Auto`](http://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonSerializer_TypeNameHandling.htm) as per http://stackoverflow.com/questions/5186973/json-serialization-of-array-with-polymorphic-objects or https://stackoverflow.com/questions/20038441/deserialize-a-listabstractclass-with-newtonsoft-json or https://stackoverflow.com/questions/29214407/deserializing-abstract-queuet-in-json/29214658#29214658 – dbc Feb 10 '16 at 23:40
  • dbc very helpful! Is there any idea how to make TypeNameHandling but without including Project name? I have following json now.. in main post – user1085907 Feb 11 '16 at 10:36

1 Answers1

2

To answer your updated question, "how do I remap assembly names when exchanging JSON data containing $type information between different .Net assemblies using Json.NET", you have a few options:

  1. The simplest would be to extract the types in question into a shared DLL and reference that DLL from both assemblies. This solves the problem and also reduces code duplication.

  2. If you cannot do this, you will need to write your own SerializationBinder, probably inheriting from Json.NET's DefaultSerializationBinder, as is described in the documentation: Custom SerializationBinder. If your classes are not generic, can simply remap the assemblyName:

    public class SimpleAssemblyMappingSerializationBinder : DefaultSerializationBinder
    {
        readonly string oldAssemblyName;
        readonly string newAssemblyName;
    
        public SimpleAssemblyMappingSerializationBinder(string oldAssemblyName, string newAssemblyName)
        {
            this.oldAssemblyName = oldAssemblyName;
            this.newAssemblyName = newAssemblyName;
        }
    
        public override Type BindToType(string assemblyName, string typeName)
        {
            if (assemblyName == oldAssemblyName)
                assemblyName = newAssemblyName;
            return base.BindToType(assemblyName, typeName);
        }
    }
    

    For a similar binder, see Handling namespace changes with TypeNameHandling.All.

    But if your classes are generic (for instance, if Data is sometimes a List<CustomClass3>), you will need to parse the generic parameters inside the type name. For an idea of how to do this see How to create a SerializationBinder for the Binary Formatter that handles the moving of types from one assembly and namespace to another. That question is about BinaryFormatter but the answer applies to Json.NET also.

  3. To omit the assembly name and namespace entirely, you could use the binder from Json serialization for Object Data Type for both serialization and deserialization.

  4. Finally, you could consider switching to DataContractJsonSerializer which exchanges type information indirectly by using contract names rather than raw .Net type names. While the data contract serializer is generally less flexible than Json.NET, its contract-based exchange of type information may be more suitable in this case. See for instance How to deserialize JSON with unnamed collection of types using DataContractSerializer.

dbc
  • 104,963
  • 20
  • 228
  • 340