Use XmlSerializer.CanDeserialize() when deserializing from string - c#

I have a method that returns object from .xml file
(please don't mind resource usage and naming, it's just an example)
public static T FromXMLFile<T>(string filePath)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
FileStream fs = new FileStream(filePath, FileMode.Open);
XmlTextReader xmlTextReader = new XmlTextReader(fs);
if(xmlSerializer.CanDeserialize(xmlTextReader))
{
object tempObject = (T)xmlSerializer.Deserialize(xmlTextReader );
xmlTextReader.Close();
return (T)tempObject;
}
else
return default(T);
}
Now I would like to do the same but with with string instead of a file. I came up with something like this (again, simplified example)
public static T FromString<T>(string inputString)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
T result;
try
{
using (TextReader reader = new StringReader(inputString))
{
result = (T)serializer.Deserialize(reader);
}
return result;
}
catch //temporary solution, finally should stick to .CanDeserialize(xmlTextReader) usage
{
return default(T);
}
}
How would I use .CanDeserialize() in this case?

Rather than using the Deserialize(TextReader) overload, create an XmlReader from the TextReader, and use that XmlReader for both the Deserialize and CanDeserialize calls:
using (TextReader reader = new StringReader(inputString))
using (XmlReader xmlReader = XmlReader.Create(reader))
{
if (serializer.CanDeserialize(xmlReader))
{
result = (T)serializer.Deserialize(xmlReader);
}
}
This approach - with both read and write - also allows you to supply additional reader/writer settings for fine-grained control of the API.

Related

C# JSON Deserialize into local variables

I have something like this:
public class MyClass_T
{
public string filename;
public string filename_txt;
public int version = 1;
public double A;
public double B;
....and so on with about 100 more variables
}
I've written the data to a file in JSON format with
public bool readFormatFile(string filename)
{
JsonSerializer serializer = new JsonSerializer();
serializer.NullValueHandling = NullValueHandling.Ignore;
using (FileStream fs = new FileStream(filename, FileMode.Create, System.IO.FileAccess.Write, FileShare.Read))
{
using (StreamWriter sw = new StreamWriter(fs))
{
using (JsonWriter writer = new JsonTextWriter(sw))
{
serializer.Serialize(writer, this);
}
}
}
}
Now I want to deserialize it. I know I can do this:
public bool writeFormatFile(string filename)
{
MyClass_T MC = new MyClass_T();
using (FileStream fs = new FileStream(filename, FileMode.Open, System.IO.FileAccess.Read, FileShare.Read))
{
using (StreamReader sr = new StreamReader(fs))
{
JsonSerializer serializer = new JsonSerializer();
serializer.NullValueHandling = NullValueHandling.Ignore;
serializer.MissingMemberHandling = MissingMemberHandling.Ignore;
MC = (MyClass_T)serializer.Deserialize(sr, typeof(MyClass_T));
}
}
}
Note that readFormatFile and writeFormatFile are part of MyClass_T. I need to get the values back into my local variables without having to do a bunch of
filename = MC.filename;
filename_txt = MC.filename_txt;
version = MC.version;
A = MC.A;
B = MC.B;
...and so on for the 100 or so variables.
Thoughts and ideas on how to proceed on this?
Like many people have made clear, it is not a very good idea to deserialize a JSON object to local variables, however if you can not create a class for the object, you can use the dynamic object type, this will allow you to deserialize the JSON into an object whilst removing the need to add any strongly types models/classes.
all you need to do is cast the deserialized result to a dynamic as you would any other object, e.g.
dynamic myObject = (dynamic)serializer.Deserialize(sr, typeof(dynamic));
this will allow you to then access myObject as you would any other object e.g.
myObject.filename for example.
i would also like to reiterate that, you really should be using a class for this as it makes code a lot more predictable, maintainable and clean in general
Instead of using an instance 'ReadFormatFile' method, you could add a static method 'ReadFormatFile' to the 'MyClass_T' class that returns the 'MyClass_T' instance that is returned from the serializer. And then you could use that method instead of calling the 'ReadFormatFile' method (or using 'new' to instantiate a 'MyClass_T' instance that is populated with the information that was previously serialized to the file).
Consider the following class:
class Demo
{
public double A;
public double B;
public void WriteFormatFile(string filename)
{
JsonSerializer serializer = new JsonSerializer();
serializer.NullValueHandling = NullValueHandling.Ignore;
using (FileStream fs = new FileStream(filename, FileMode.Create, System.IO.FileAccess.Write, FileShare.Read))
{
using (StreamWriter sw = new StreamWriter(fs))
{
using (JsonWriter writer = new JsonTextWriter(sw))
{
serializer.Serialize(writer, this);
}
}
}
}
public static Demo ReadFormatFile(string filename)
{
using (FileStream fs = new FileStream(filename, FileMode.Open, System.IO.FileAccess.Read, FileShare.Read))
{
using (StreamReader sr = new StreamReader(fs))
{
JsonSerializer serializer = new JsonSerializer();
serializer.NullValueHandling = NullValueHandling.Ignore;
serializer.MissingMemberHandling = MissingMemberHandling.Ignore;
return (Demo)serializer.Deserialize(sr, typeof(Demo));
}
}
}
}
and its use elsewhere:
const string filename = "demo.json";
var d = new Demo();
d.A = 2;
d.B = 3;
d.WriteFormatFile(filename);
d.A = 4;
// replace d.ReadFormatFile(filename); with the following line
d = Demo.ReadFormatFile(filename);
Console.WriteLine(d.A);
The output will be '2', the restored value for the 'A' field.

Passing a class as type

I am trying to pass in a type object to pass to the serializer.
internal void SerializeXML(Object ObjType, String XMLRoot, Object Output, String Filename)
{
XmlSerializer serializer = new XmlSerializer(typeof(ObjType), new XmlRootAttribute(XMLRoot));
StreamReader reader = new StreamReader(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(Filename));
Output = (Type)serializer.Deserialize(reader);
reader.Close();
}
And I want to call it by (Main.LanguageList.Language is a class):
SerializeXML(Main.LanguageList.Language, "Language", LanguageListFile, InternalLangListXML);
I am getting Object is a variable but is used as a type.
It'd be more elegant to write a generic method:
internal void DeserializeXML<T>(String XMLRoot, T Output, String Filename)
{
XmlSerializer serializer = new XmlSerializer(typeof(T), new XmlRootAttribute(XMLRoot));
StreamReader reader = new StreamReader(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(Filename));
Output = (T)serializer.Deserialize(reader);
reader.Close();
}
And call it like this:
DeserializeXML<Main.LanguageList.Language>("Language", LanguageListFile, InternalLangListXML);
Also, it'd suggest changing the method to return the result instead of relying on an output parameter:
internal T DeserializeXML<T>(String XMLRoot, String Filename)
{
XmlSerializer serializer = new XmlSerializer(typeof(T), new XmlRootAttribute(XMLRoot));
using (StreamReader reader = new StreamReader(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(Filename)))
{
return (T)serializer.Deserialize(reader);
}
}
It can then be called like this:
var result = DeserializeXML<Main.LanguageList.Language>("Language", InternalLangListXML);

Reading twice from FileStream

I have a GetServiceMap() method which calls deserializer who then opens the stream and reads something from it.
The problem is that i have a GetAllGroups() method also who calls deserializer over the same stream.
How would i syncronize it? With ManualResetEvent maybe?
public ServiceMapModel GetServiceMap()
{
s._mre.WaitOne();
return s.Deserialize();
}
public List<Group> GetAllGroups()
{
s._mre.WaitOne();
return s.Deserialize().Groups;
}
Deserialize method:
public ManualResetEvent _mre = new ManualResetEvent(true);
public ServiceMapModel Deserialize()
{
_serviceMap = new ServiceMapModel();
_mre.Reset();
try
{
using (var fileStream = new FileStream(Settings.Path, FileMode.Open))
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreComments = true;
using (XmlReader reader = XmlReader.Create(fileStream, settings))
{
_serviceMap = _serializer.Deserialize(reader) as ServiceMapModel;
}
fileStream.Close();
}
}
catch (IOException)
{
}
_mre.Set();
return _serviceMap;
}
For your case basic lock should be enough - no reason to use more complicated objects.
I would actually cache result of deserialization instead of reading from file every time, but it is your call.

How do I parse generic data types from a file?

I have a generic class as follows:
class myClass<T>
{
public T[] m_SomeData;
}
I want to implement a generic method to read data from a file and populate the data fields of this class. Something like:
class myClass<T>
{
public T[] m_SomeData;
public void ReadData(string fileName);
}
An implementation of the ReadData methods looks something like this (all error checking removed for brevity):
void ReadData(string fileName)
{
TextReader rdr = new StreamReader(fileName);
string line = rdr.ReadLine();
// Here I need to parse value of type T from the line
// and initialize the m_SomeData array
// How do I do that? I would like to keep it generic if possible
}
Note, I can guarantee the type T is numeric, at least by convention
Update: OP would like human readable output. I would suggest JavaScriptSerializer, then, in:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Web.Extensions.dll
// Serialize:
using (var fs = new FileStream(fileName, FileMode.Create))
using (var writer = new StreamWriter(fs))
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
string s = serializer.Serialize(m_SomeData);
writer.Write(s);
}
// Deserialize:
using (var fs = new FileStream(fileName, FileMode.Open))
using (var reader = new StreamReader(fs))
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
var s = reader.ReadToEnd();
m_SomeData = serializer.Deserialize<T[]>(s);
}
Old Answer:
This is a job for BinaryFormatter:
using (FileStream fs = new FileStream(fileName, FileMode.Open))
{
BinaryFormatter formatter = new BinaryFormatter();
m_SomeData = (T[])formatter.Deserialize(fs);
}
This of course assumes you are also using it to serialize via formatter.Serialize(fs, m_SomeData);.

Create SqlXml object instead of string object

I have a method:
public static string UnZipStr(byte[] input)
{
if (input == null){
return null;
}
using (MemoryStream inputStream = new MemoryStream(input))
using (DeflateStream gzip = new DeflateStream(inputStream, CompressionMode.Decompress))
using (StreamReader reader = new StreamReader(gzip, System.Text.Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
But I always get xml text unzipping, it is fact.
I need to change this method in order to return SqlXml object.
Unfortunate I'm java developer and cannot solve this task.
Do you need a SqlXml object or an XmlDocument/XDocument object? This post about converting a SqlXml object into an XmlDocument may be related to your needs.
You may be able to do the following:
public static string SqlXmlFromZippedBytes(byte[] input)
{
if (input == null){
return null;
}
using (MemoryStream inputStream = new MemoryStream(input))
using (DeflateStream gzip = new DeflateStream(inputStream, CompressionMode.Decompress))
using (StreamReader reader = new StreamReader(gzip, System.Text.Encoding.UTF8))
{
return new SqlXml(reader); // From System.Data.SqlTypes
}
}
Here is the documentation on the SqlXml constructor.

Categories