Unable to deserialize Json using DataContractJsonSerializer - c#

My model:
internal class Order
{
internal string OrderId;
internal string ShippingId;
internal Date OrderDate;
internal double Amount;
}
My method that uses DataContractJsonSerializer :
Order GetOrder(string json)
{
Order obj;
using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
DataContractJsonSerializer deserializer = new DataContractJsonSerializer(typeof(Order));
obj = (Order) deserializer.ReadObject(ms);
}
return obj;
}
But its not able to deserialize it? I cant understand why its giving me exception always!

Related

ignore xml-attribut when deserialising [duplicate]

Can I make XmlSerializer ignore the namespace (xmlns attribute) on deserialization so that it doesn't matter if the attribute is added or not or even if the attribute is bogus? I know that the source will always be trusted so I don't care about the xmlns attribute.
Yes, you can tell the XmlSerializer to ignore namespaces during de-serialization.
Define an XmlTextReader that ignores namespaces. Like so:
// helper class to ignore namespaces when de-serializing
public class NamespaceIgnorantXmlTextReader : XmlTextReader
{
public NamespaceIgnorantXmlTextReader(System.IO.TextReader reader): base(reader) { }
public override string NamespaceURI
{
get { return ""; }
}
}
// helper class to omit XML decl at start of document when serializing
public class XTWFND : XmlTextWriter {
public XTWFND (System.IO.TextWriter w) : base(w) { Formatting= System.Xml.Formatting.Indented;}
public override void WriteStartDocument () { }
}
Here's an example of how you would de-serialize using that TextReader:
public class MyType1
{
public string Label
{
set { _Label= value; }
get { return _Label; }
}
private int _Epoch;
public int Epoch
{
set { _Epoch= value; }
get { return _Epoch; }
}
}
String RawXml_WithNamespaces = #"
<MyType1 xmlns='urn:booboo-dee-doo'>
<Label>This document has namespaces on its elements</Label>
<Epoch xmlns='urn:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'>0</Epoch>
</MyType1>";
System.IO.StringReader sr;
sr= new System.IO.StringReader(RawXml_WithNamespaces);
var s1 = new XmlSerializer(typeof(MyType1));
var o1= (MyType1) s1.Deserialize(new NamespaceIgnorantXmlTextReader(sr));
System.Console.WriteLine("\n\nDe-serialized, then serialized again:\n");
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("urn", "booboo-dee-doo");
s1.Serialize(new XTWFND(System.Console.Out), o1, ns);
Console.WriteLine("\n\n");
The result is like so:
<MyType1>
<Label>This document has namespaces on its elements</Label>
<Epoch>0</Epoch>
</MyType1>
If you expect no namespace, but the input has namespaces, then you can set
Namespaces = false
on your XmlTextReader.
Exdended Wolfgang Grinfeld answer (w/o exception handling):
public static Message Convert(XmlDocument doc)
{
Message obj;
using (TextReader textReader = new StringReader(doc.OuterXml))
{
using (XmlTextReader reader = new XmlTextReader(textReader))
{
reader.Namespaces = false;
XmlSerializer serializer = new XmlSerializer(typeof(Message));
obj = (Message)serializer.Deserialize(reader);
}
}
return obj;
}
Solved this by using XmlSerializer Deserialize to read from xml instead from stream. This way before xml is Deserialized, using Regex to remove xsi:type from the xml. Was doing this is Portable Class Library for Cross Platform, so did not had many other options :(. After this the deserialization seems to work fine.
Following code can help,
public static TClass Deserialize<TClass>(string xml) where TClass : class, new()
{
var tClass = new TClass();
xml = RemoveTypeTagFromXml(xml);
var xmlSerializer = new XmlSerializer(typeof(TClass));
using (TextReader textReader = new StringReader(xml))
{
tClass = (TClass)xmlSerializer.Deserialize(textReader);
}
return tClass;
}
public static string RemoveTypeTagFromXml(string xml)
{
if (!string.IsNullOrEmpty(xml) && xml.Contains("xsi:type"))
{
xml = Regex.Replace(xml, #"\s+xsi:type=""\w+""", "");
}
return xml;
}
Why try to make the XmlSerializer forget how XML works? It's a fact of XML that two elements with the same name but different namespaces are different elements.
If you want to process XML that has no namespaces, then you should pre-process it to remove the namespaces, and then pass it to the serializer.

Deserialize property with a different name?

I have an interface with exposes a property called Pages:
public interface INameSet
{
IQueryable<string> Names { get; }
}
I have this class which implements the interface and must also be parsed from a JSON object:
[DataContract(Name = "surveyPageSet")]
public class SurveyPage : INameSet
{
[DataMember(Name = "names")]
public List<string> SurveyNames { get; set; }
public IQueryable<string> Names
{
get
{
//Returns SurveyNames after some filtration logic
}
}
}
My problem is that when I pass in this object:
{
"names": ["testname"]
}
The JSON interpreter is trying to deserialize it to match the Names property instead of the SurveyNames property. I know this happens because when removing the implementation of the interface and changing SurveyNames to Names it populates the property fine. Is there any way to get it to serialize to the correct property or do I need to create a translator class that will generate the proper concretion of the INameSet interface?
EDIT: This is with the built-in serializer. If there is a solution with Newtonsoft/JSON.NET that would be fine with me.
JavaScriptSerializer doesn't allow for remapping of names out of the box, so don't use it.
Instead, use Json.NET or DataContractJsonSerializer. In fact, both should already work given the data contract attributes you have applied.
For instance, using Json.NET, if I do:
var page1 = JsonConvert.DeserializeObject<SurveyPage>(json);
Debug.Assert(page1.SurveyNames != null && page1.SurveyNames.SequenceEqual(new string [] { "testname" }));
Then there is no assert. Similarly there is no assert if I do:
var page2 = DataContractJsonSerializerHelper.GetObject<SurveyPage>(json);
Debug.Assert(page2.SurveyNames != null && page2.SurveyNames.SequenceEqual(new string[] { "testname" }));
using the helper class:
public static class DataContractJsonSerializerHelper
{
private static MemoryStream GenerateStreamFromString(string value)
{
return new MemoryStream(Encoding.Unicode.GetBytes(value ?? ""));
}
public static string GetJson<T>(T obj, DataContractJsonSerializer serializer)
{
using (var memory = new MemoryStream())
{
serializer.WriteObject(memory, obj);
memory.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(memory))
{
return reader.ReadToEnd();
}
}
}
public static string GetJson<T>(T obj) where T : class
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
return GetJson(obj, serializer);
}
public static T GetObject<T>(string json) where T : class
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
return GetObject<T>(json, serializer);
}
public static T GetObject<T>(string json, DataContractJsonSerializer serializer)
{
T obj = default(T);
using (var stream = GenerateStreamFromString(json))
{
obj = (T)serializer.ReadObject(stream);
}
return obj;
}
}
Update
If you really want to continue to use JavaScriptConverter, you can write your own JavaScriptConverter and deserialize each field manually. But it's a bother and I wouldn't recommend it.

Polymorphism WCF deserialization doesn't work

I have created the following classes. one base class IObject and 2 derived classes A and B.
[KnownType(typeof(B))]
[KnownType(typeof(A))]
[DataContract(Name = "IObject")]
public class IObject
{
}
[DataContract(Name="A")]
public class A : IObject
{
[DataMember]
public string s1 { get; set; } // Tag Name as it will be presented to the user
}
[DataContract(Name="B")]
public class B : IObject
{
[DataMember]
public string s2 { get; set; }
}
I have also created the following Service:
[ServiceKnownType(typeof(B))]
[ServiceKnownType(typeof(A))]
public void GetR(IObject obj)
{
B other = (B)obj;
}
What i want to do is get an instance of A or B but i don't know which one i will get so i expect to get an IObject and cast it later to either A or B as shown in the example i put.
What happens when i send a json string containing s2 string is that i get IObject instance and not B instance.
What is wrong with the code?
Example of the client i'm using :
var url = serviceURL;
var data = {s2: "Data"};
var json = JSON.stringify(data);
$.ajax({
type: "POST",
url: url,
data: data,
contentType: "application/json",
dataType: 'json'
});
Edited: I have uploaded an example code to gitHub at the following link:
https://github.com/AlgoEitan/27588144
The client there is a c# client (I've tried it with both c# and javascript - web browser client)
DataContractJsonSerializer has a specific format in which it stores hints about known type information for polymorphic types, which one can discover with a bit of testing. I copied and pasted your classes as-is, and created the following test code:
public static class DataContractJsonSerializerPolymorphismTest
{
public static void Test()
{
var a1 = new A() { s1 = "A" };
var b1 = new B() { s2 = "B" };
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(IObject));
var jsona1 = DataContractJsonSerializerHelper.GetJson(a1, serializer);
var jsonb1 = DataContractJsonSerializerHelper.GetJson(b1, serializer);
Debug.WriteLine(jsona1);
Debug.WriteLine(jsonb1);
var newa1 = DataContractJsonSerializerHelper.GetObject<IObject>(jsona1, serializer);
Debug.Assert(newa1.GetType() == a1.GetType()); // No assert
}
}
With that, the following JSON was created:
{"__type":"A:#Tile.DataContractJsonSerializerPolymorphism","s1":"A"}
{"__type":"B:#Tile.DataContractJsonSerializerPolymorphism","s2":"B"}
Where Tile.DataContractJsonSerializerPolymorphism happens to be the name of the CLR namespace in my test application. So, as you can see, the known type information got tucked away in this extra JSON property __type. Now, if you were using DataContractJsonSerializerHelper in your client also, you would never know this, because communication would just work. But you are using JSON.stringify() which does not have this logic. So, you may have to manually add the "__type":"DataContractName:DataContractNamespace" property on the client side.
More about the format for polymorphic types can be found here. (I only tracked this documentation down after finding the hidden __type parameter, which gave me an extra Google search term.)
FYI, here is the helper class I used with the test code:
public static class DataContractJsonSerializerHelper
{
private static MemoryStream GenerateStreamFromString(string value)
{
return new MemoryStream(Encoding.Unicode.GetBytes(value ?? ""));
}
public static string GetJson<T>(T obj, DataContractJsonSerializer serializer) where T : class
{
using (var memory = new MemoryStream())
{
serializer.WriteObject(memory, obj);
memory.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(memory))
{
return reader.ReadToEnd();
}
}
}
public static string GetJson<T>(T obj) where T : class
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
return GetJson(obj, serializer);
}
public static T GetObject<T>(string json) where T : class
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
return GetObject<T>(json, serializer);
}
public static T GetObject<T>(string json, DataContractJsonSerializer serializer) where T : class
{
T obj = null;
using (var stream = GenerateStreamFromString(json))
{
obj = (T)serializer.ReadObject(stream);
}
return obj;
}
}

Deserializing XML from String

I'm trying to convert the result I get from my web service as a string and convert it to an object.
This is the string I'm getting from my service:
<StatusDocumentItem><DataUrl/><LastUpdated>2013-01-31T15:28:13.2847259Z</LastUpdated><Message>The processing of this task has started</Message><State>1</State><StateName>Started</StateName></StatusDocumentItem>
So I have a class for this as:
[XmlRoot]
public class StatusDocumentItem
{
[XmlElement]
public string DataUrl;
[XmlElement]
public string LastUpdated;
[XmlElement]
public string Message;
[XmlElement]
public int State;
[XmlElement]
public string StateName;
}
And this is how I'm trying to get that string as an object of type StatusDocumentItem with XMLDeserializer (NB. operationXML contains the string):
string operationXML = webRequest.getJSON(args[1], args[2], pollURL);
var serializer = new XmlSerializer(typeof(StatusDocumentItem));
StatusDocumentItem result;
using (TextReader reader = new StringReader(operationXML))
{
result = (StatusDocumentItem)serializer.Deserialize(reader);
}
Console.WriteLine(result.Message);
But my result object is always empty. What am I doing wrong?
Update. The value I get from my operationXML is like this and has an unnecessary xmlns attribute that is blocking my deserialization. Without that attribute, everything is working fine. Here is how it looks like:
"<StatusDocumentItem xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"><DataUrl/><LastUpdated>2013-02-01T12:35:29.9517061Z</LastUpdated><Message>Job put in queue</Message><State>0</State><StateName>Waiting to be processed</StateName></StatusDocumentItem>"
Try this:
string xml = "<StatusDocumentItem xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"><DataUrl/><LastUpdated>2013-02-01T12:35:29.9517061Z</LastUpdated><Message>Job put in queue</Message><State>0</State><StateName>Waiting to be processed</StateName></StatusDocumentItem>";
var serializer = new XmlSerializer(typeof(StatusDocumentItem));
StatusDocumentItem result;
using (TextReader reader = new StringReader(xml))
{
result = (StatusDocumentItem)serializer.Deserialize(reader);
}
Console.WriteLine(result.Message);
Console.ReadKey();
Does it show "Job put in queue"?
This generic extension works well for me....
public static class XmlHelper
{
public static T FromXml<T>(this string value)
{
using TextReader reader = new StringReader(value);
return (T) new XmlSerializer(typeof(T)).Deserialize(reader);
}
}

JSON deseralization to abstract list using DataContractJsonSerializer

I'm trying to deserialize a JSon file to an instance of a class that contains an abstract list. Serializing the instance to the Json works well (check the json file below). When deserializing I get a "System.MemberAccessException" with the message "Cannot create an abstract class". Obvisouly the deseralizer is trying to instantiate the abstract class and not the concrete class.
In my example the deserialized class is called ElementContainer :
namespace Data
{
[DataContract]
[KnownType(typeof(ElementA))]
[KnownType(typeof(ElementB))]
public class ElementContainer
{
[DataMember]
public List<Element> Elements { get; set; }
}
[DataContract]
public abstract class Element
{
}
[DataContract]
public class ElementA : Element
{
[DataMember]
int Id { get; set; }
}
[DataContract]
public class ElementB : Element
{
[DataMember]
string Name { get; set; }
}
}
This is the Json file that was serialized and that I'm trying to deserialize. Notice the "__type" field for the deserializer to create the concrete classes :
{
"Elements":
[
{
"__type":"ElementA:#Data",
"Id":1
},
{
"__type":"ElementB:#Data",
"Name":"MyName"
}
]
}
The following is the code I'm using for deserialization :
public T LoadFromJSON<T>(string filePath)
{
try
{
using (FileStream stream = File.OpenRead(filePath))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
T contract = (T)serializer.ReadObject(stream);
return contract;
}
}
catch (System.Exception ex)
{
logger.Error("Cannot deserialize json " + filePath, ex);
throw;
}
}
It is possible to make the deserialization work ?
Thanks !
We've found why it wasn't working. Just after the serialization of the object we ident the resulting string for more readability. Then we write the string into a file :
public void SaveContractToJSON<T>(T contract, string filePath)
{
using (MemoryStream stream = new MemoryStream())
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
serializer.WriteObject(stream, contract);
string json = Encoding.UTF8.GetString(stream.ToArray());
File.WriteAllText(filePath, json.IndentJSON());
}
}
The identation is actually the reason why the deserialization was not working. It seems the parser of the DataContractJsonSerializer is really picky. If some characters are between the character { and the field "__type", the serializer get lost.
For example this string will serialize correctly :
"{\"Elements\":[{\"__type\":\"ElementA:#Data\",\"Id\":1}]}"
But this next string will not serialize.
"{\"Elements\":[ {\"__type\":\"ElementA:#Data\",\"Id\":1}]}"
The only difference is the space characters before the "__type". The serialization will throw a MemberAccessException. This is misleading because this behavior appears only when deserializing into an abstract List. Serializing into an abstract field works fine no matter the characters.
To fix this issue without removing the readability of the file, The string can be modified before the deseralization. For example :
public T LoadContractFromJSON<T>(string filePath)
{
try
{
string text = File.ReadAllText(filePath);
text = Regex.Replace(text, "\\{[\\n\\r ]*\"__type", "{\"__type");
using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(text)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
T contract = (T)serializer.ReadObject(stream);
return contract;
}
}
catch (System.Exception ex)
{
logger.Error("Cannot deserialize json " + filePath, ex);
throw;
}
}

Categories