I need to deserialize Dictionary<> in C#. Since, XmlSerializer cannot be used for generic Dictionary type, I thought of using DataContractSerializer.I had a StreamReader in which the data was to be deserialized, but it showed unmatching overload parameters on using with DataContractSerializer.ReadObject.
My previous code was:
StreamReader isr = new StreamReader(File.OpenRead(algorithmName + ".conf"));
configuration.Load(isr);
The Load method is defined as:
public void Load(StreamReader isr)
{
DataContractSerializer dcs = new DataContractSerializer(typeof(the class containing this method));
XmlDictionaryReader read = XmlDictionaryReader.CreateTextReader(isr, new XmlDictionaryReaderQuotas());
dcs.ReadObject(isr);
}
The Load method is contained in class that implements Dictionary<>
But this showed parameter mismatched error due to StreamReader as parameter for ReadObject().
So, I modified the code as:
FileStream isr = File.OpenRead(algorithmName + ".conf");
configuration.Load(isr);
And the Load method as:
public void Load(FileStream isr)
{
DataContractSerializer dcs = new DataContractSerializer(typeof(the class containing Load method));
XmlDictionaryReader read = XmlDictionaryReader.CreateTextReader(isr, new XmlDictionaryReaderQuotas());
dcs.ReadObject(isr);
}
Now there are no errors but I wanted to know whether using DataContractSerializer in place of XmlSerializer is correct or not. Also, any updates on replacing streamreader with filestream would be highly appreciated.
Related
I'm using serialization to a string as follows.
public static string Stringify(this Process self)
{
XmlSerializer serializer = new XmlSerializer(typeof(Process));
using (StringWriter writer = new StringWriter())
{
serializer.Serialize(writer, self,);
return writer.ToString();
}
}
Then, I deserialize using this code. Please note that it's not an actual stringification from above that's used. In our business logic, it makes more sense to serialize a path, hence reading in from said path and creating an object based on the read data.
public static Process Processify(this string self)
{
XmlSerializer serializer = new XmlSerializer(typeof(Process));
using (XmlReader reader = XmlReader.Create(self))
return serializer.Deserialize(reader) as Process;
}
}
This works as supposed to except for a small issue with encoding. The string XML that's produced, contains the addition encoding="utf-16" as an attribute on the base tag (the one that's about XML version, not the actual data).
When I read in, I get an exception because of mismatching encodings. As far I could see, there's no way to specify the encoding for serialization nor deserialization in any of the objects I'm using.
How can I do that?
For now, I'm using a very brute work-around by simply cutting of the excessive junk like so. It's Q&D and I want to remove it.
public static string Stringify(this Process self)
{
XmlSerializer serializer = new XmlSerializer(typeof(Process));
using (StringWriter writer = new StringWriter())
{
serializer.Serialize(writer, self,);
return writer.ToString().Replace(" encoding=\"utf-16\"", "");
}
}
I serialise my wcf requests and responses into XML but to save database space, I strip out all non-essential information, so the result is:
<someObject>
<someValue>10</someValue>
</someObject>
There are more complex nested properties, the above is just an example.
When I try to deserialise, I get an error saying expecting someObject, someNamespace but encountered someObject, ''
byte[] data = System.Text.Encoding.UTF8.GetBytes(xmlString);
stream.Write(data, 0, data.Length);
stream.Position = 0;
DataContractSerializer deserializer = new DataContractSerializer(typeof(T));
return deserializer.ReadObject(stream) as T;
Is there an easy way to solve this? Perhaps by not using a DataContractSerializer?
I know this post is almost a week old, but it may help someone if you've already found a solution.
If you know the structure beforehand, you could use the XmlSerializer class.
string xml = "<someObject>" +
" <someValue>10</someValue>" +
"</someObject>";
using (TextReader reader = new StringReader(xml))
{
XmlSerializer serializer = new XmlSerializer(typeof(someObject));
var obj = serializer.Deserialize(reader);
}
Here's the supporting class to deserialize it into:
[Serializable()]
public class someObject
{
[XmlElement("someValue")]
public string someValue { get; set; }
}
For more complex XML, you'd have to modify the class that you're using. You can nest classes as well as support arrays/lists.
I am using the following function to attempt to serialize an object to XML..
public static string SerializeObject<T>(T obj)
{
try
{
string xmlString = null;
MemoryStream memoryStream = new MemoryStream();
XmlSerializer xs = new XmlSerializer(typeof(T));
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
xs.Serialize(xmlTextWriter, obj);
memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
xmlString = UTF8ByteArrayToString(memoryStream.ToArray()); return xmlString;
}
catch (Exception ex)
{
return string.Empty;
}
}
When attempting to serialize an object that has an IList property in it, I get the following exception..
Cannot serialize member 'ObjectModel.Order.LineItems' of type 'System.Collections.Generic.IList
Can someone help me change my function to accommodate for this scenario?
Is there anything I can do this existing code to look into the input object. If its of type Ilist change it to a List? Can somoeone help me with code for that if its at all possible??
There's no great solution for this, only the workaround of using a concrete type like List<T> in this case - you could either change the existing property to be List<T> or add an additional property used just for serialization of type List<T> (and XML-ignore your existing property).
XmlSerializer does not handle properties of type IList<T>. There are some workarounds, the most straightforward of which is to change the type of the property:
https://www.google.com/search?q=xmlserializer+ilist
How important is Xml output? binary format is more accomodating. you could convert the output to base64 string if needed.
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatters.binary.binaryformatter(v=vs.71).aspx
I am given a stream of json data which contains a field named "type". This type field describes the type of object that needs to be created at runtime. It looks like I am unable to use the JsonTextReader twice and I cannot find away to reset the text reader to the beginning.
using (var streamReader = new StreamReader(stream, Encoding))
using (var jsonTextReader = new JsonTextReader(streamReader))
{
JToken token = JObject.Load(jsonTextReader);
var type = (string) token.SelectToken("type");
var modelType = Type.GetType("Project." + type + ", Project");
// Fails here
var obj = serializer.Deserialize(jsonTextReader, modelType);
}
I get this error message.
Unexpected token while deserializing object: EndObject.
You can create a JsonReader from the JToken.
JsonReader reader = token.CreateReader();
To reset your reader to the begginning, set the Position property of the underlying stream to 0.
streamReader.BaseStream.Position = 0;
Edit:
While this will reset your underlying stream, the jsonTextReader is forward-only by definition, which means its line number and position are readonly. For this to work you would have to reset the streamReader position, then feed it into a new JsonTextReader object.
So unfortunately Phil, there is no way to read the JsonTextReader twice since it is forward-only.
Reference:
http://james.newtonking.com/projects/json/help/html/T_Newtonsoft_Json_JsonTextReader.htm
"Represents a reader that provides fast, non-cached, forward-only access to serialized Json data."
I cover using the JsonTextReader in a memory-efficient format, avoiding the Large Object Heap, etc., in my blog, as per James Newton King's recommendations. You can leverage this and the supplied code to read your JSON multiple times without worrying about the underlying implementation of JsonTextReader.
Comments and feedback always welcome.
I did some more testing and found that the following works.
Set JsonTextReader.CloseInput = false
Destroy the JsonTextReader (by closing the using statement)
Set StreamReader.BaseStream.Position = 0
Create a new JsonTextReader
It would look something like this:
using (var streamReader = new StreamReader(stream, encoding))
{
Type modelType = null;
using (var jsonTextReader = new JsonTextReader(streamReader))
{
jsonTextReader.CloseInput = false;
JToken token = JObject.Load(jsonTextReader);
string type = (string)token.SelectToken("type");
modelType = Type.GetType("Project." + type + ", Project");
}
streamReader.BaseStream.Position = 0;
using (var jsonTextReader = new JsonTextReader(streamReader))
{
var obj = serializer.Deserialize(jsonTextReader, modelType);
}
}
If there is an object in which every public property must be serialized and properties are simple (just numbers or strings or objects already implementing ISerializable), is there an easy way to do it without having to create GetObjectData(SerializationInfo info, StreamingContext context) and a constructor taking SerializationInfo as argument every time?
I know that it can be done manually with reflection, but is there a magic method inside the .NET Framework to do it?
So the correct answer is:
Don't try to implement ISerializable - it is for custom serialization. Instead add the [Serializable] attribute right before your class declaration.
Try the BinaryFormatter class - should do what you need
EDIT: You do not derive from BinaryFormatter - it is a utility class you use to do your serialization. Here is an example copied from the docs
MyObject obj = new MyObject();
obj.n1 = 1;
obj.n2 = 24;
obj.str = "Some String";
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream("MyFile.bin", FileMode.Create, FileAccess.Write, FileShare.None);
formatter.Serialize(stream, obj);
stream.Close();