Missing strings in XmlBinaryWriterSession after DataContractSerializer.WriteObject - c#

say I have:
[DataContract(Name=,Namespace=)]
[KnownType(typeof(B))]
class A
{
[DataMember]
public B B{get;set;}
}
[DataContract(Name=,Namespace=)]
class B
{
public string Something{get;set;}
}
...
// ms = memory stream
// writerSession = XmlBinaryWriterSession
using (var writer = XmlDictionaryWriter.CreateBinaryWriter(ms, null, writerSession))
{
var serializer = new DataContractSerializer(typeof(SerializedFeedItem));
serializer.WriteObject(writer, serItem);
writer.Flush();
...
}
For now I see that only type A's xml dictionary strings are added to the writer session. I wonder how can I make the serializer write type B's strings as well.
The question is related to compressing serialized content with XmlDictionary-related technique.
Thanks in advance.

Related

Saving a Dictionary<int, object> in C# - Serialization?

I have a dictionary in c#
private Dictionary<int, UserSessionInfo> userSessionLookupTable = new Dictionary<int, UserSessionInfo>();
Now I have created a dictionary object
this.userSessionLookupTable.Add(userSessionInfoLogin.SessionId, userSessionInfoLogin);
Now I want a generic method to serialize and de-serialize the dictionory to byte array.
Like
public static void Serialize(Dictionary<int, object> dictionary, Stream stream)
{
//Code here
}
and
public static static Dictionary<int, object> Deserialize(Stream stream)
{
//Code here
}
Can anyone help me on this??
Try this....
public static void Serialize<Object>(Object dictionary, Stream stream)
{
try // try to serialize the collection to a file
{
using (stream)
{
// create BinaryFormatter
BinaryFormatter bin = new BinaryFormatter();
// serialize the collection (EmployeeList1) to file (stream)
bin.Serialize(stream, dictionary);
}
}
catch (IOException)
{
}
}
public static Object Deserialize<Object>(Stream stream) where Object : new()
{
Object ret = CreateInstance<Object>();
try
{
using (stream)
{
// create BinaryFormatter
BinaryFormatter bin = new BinaryFormatter();
// deserialize the collection (Employee) from file (stream)
ret = (Object)bin.Deserialize(stream);
}
}
catch (IOException)
{
}
return ret;
}
// function to create instance of T
public static Object CreateInstance<Object>() where Object : new()
{
return (Object)Activator.CreateInstance(typeof(Object));
}
Usage...
Serialize(userSessionLookupTable, File.Open("data.bin", FileMode.Create));
Dictionary<int, UserSessionInfo> deserializeObject = Deserialize<Dictionary<int, UserSessionInfo>>(File.Open("data.bin", FileMode.Open));
I have used 'Object' in the code above to fulfil your requirements but personally I would use 'T' which usually denotes a generic object in C#
You could use a MemoryStream for this purpose unless you don't want to create a file. But for the serializing method, if you don't want to return a value you should probably mark the stream parameter as [Out].
public static void Serialize(Dictionary<int, object> dictionary, [Out] Stream stream)
{
stream = new MemoryStream();
new BinaryFormatter().Serialize(stream, dictionary);
}
public static Dictionary<int, object> Deserialize(Stream stream)
{
return (Dictionary<int, object>)new BinaryFormatter().Deserialize(stream);
}
This will perform binary serialization.
EDIT:
To get it as a byte array you can just cast the stream returned from the Serialize() method to a MemoryStream, and then call .ToArray() on it.
This is an example:
MemoryStream outputStream;
Serialize(your dictionary here, outputStream);
byte[] bytes = outputStream.ToArray();
A bit late to answer maybe but here is a practical and more general answer from my experience: Use simple JSON serialisation if you want something quick and easy or if there is not much demand for data size or performance. If you want something highly space efficient, fast and cross-platform as well, use Google Protocol Buffers.
JSON
string output = JsonConvert.SerializeObject(myObject);
var copyOfMyObject = JsonConvert.DeserializeObject<MyObject>(output);
There are a lot of ways you can control the serialisation and use streams and so on.
Json.Net also supports dictionaries too.
Protobuf
There are two ways of using protobuf in c#: Using .proto message definitions and generate code or reflection based approach using your class definitions and attributes. It depends on your project but I prefer the message definitions personally.
Note that you might have to do your own munging before and after serialisation and deserialisation to use with a Dictionary.
Message definition based: using proto3
.proto file:
message Person {
int32 id = 1;
string name = 2;
}
Generate code using Google Protobuf:
protoc --csharp_out=$DST_DIR $SRC_DIR/person.proto
Serialise / deserialise using generated classes:
Person john = ...;
john.WriteTo(stream);
var copyOfJohn = Person.Parser.ParseFrom(stream);
Also, a quick note to let you know that there is probably a less known feature of proto3 is the capability of using JSON.
Reflection based: Protobuf-net
Here is a quick example (copied from protobuf-net page)
[ProtoContract]
class Person {
[ProtoMember(1)]
public int Id {get;set;}
[ProtoMember(2)]
public string Name {get;set;}
}
Serializer.Serialize(file, person);
newPerson = Serializer.Deserialize<Person>(file);
Binary, text, streams, oh my!
Apologies, I sort of ignored the question about 'Binary' serialisation (and was not a specific BinaryFormatter question) since there are lots of ways you can use streams to deal with data flowing in and out of a process. C# stream, reader and writer and other System.IO APIs are quite well documented.
For Binary Serialization
For more info take a look at BinaryFormatter.
Here is a possible solution:
public void Serialize(Dictionary<int, UserSessionInfo> dictionary, Stream stream)
{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(dictionary.Count);
foreach (var obj in dictionary)
{
writer.Write(obj.Key);
writer.Write(obj.Value);
}
writer.Flush();
}
public Dictionary<int, UserSessionInfo> Deserialize(Stream stream)
{
BinaryReader reader = new BinaryReader(stream);
int count = reader.ReadInt32();
var dictionary = new Dictionary<int, UserSessionInfo>(count);
for (int n = 0; n < count; n++)
{
var key = reader.ReadInt32();
var value = reader.ReadString();
dictionary.Add(key, value);
}
return dictionary;
}
but you still need to have UserSessionInfo ToString() converter;
For XML Serialization
Create a sample class Session
public class Session
{
[XmlAttribute]
public int SessionID;
[XmlAttribute]
public UserSessionInfo SessionInfo;
}
Then you can create XmlSerializer if you want to serialize it as XML
XmlSerializer serializer = new XmlSerializer(
typeof(Session[]),
new XmlRootAttribute() { ElementName = "sessions" }
);
And now you can serialize or deserialize.
Serialization:
serializer.Serialize(
stream,
dict.Select(kv => new Session(){SessionID = kv.Key, SessionInfo = kv.Info}).ToArray()
);
Deserialization:
var deserialized = (
(Session[])serializer.Deserialize(stream)
).ToDictionary(i => i.id, i => i.info);
But you need to have ToString() method in your UserSessionInfo to store it in the XML.
And the XML may look like this:
<sessions>
<session id='int_goes_here' value='string_goes_here'/>
</sessions>
Hope this helps.
unless you want to build your own completely custom serializer then you need to use the built in serialization structure
The dictionary and all the referenced types must implement the inbuilt serialisation structure which requires classes to be decorated witht the correct attributes ie [Serializable], or [DataContact] and as object can't implement it then you can't serialise it directly, you need to change it to a serializable type

Different field serialization technique in ntext SQL field while keeping backward compatibility

TL, DR: we've been serializing some data in some tables of our SQL DB. Unfortunately that serialization technique wastes a lot of space for markup characters. We've found a new, more efficient way: to keep backwards compatibility, is it safe to adopt the following logic? -> When the serialization occurs, it always occurs using the new, more efficient way. When deserialization occurs, we check whether the string uses the new serialization format or the old one --> we then deserialize with the appropriate method. Is this robust? Can this be used in production? Aren't there any subtle problems with this approach?
Greetings. I'm working on an application which interacts with a SQL database. To achieve a specific business requirement, we've been serializing some data in a special column of our DB tables, of type ntext. Basically, in each cell of this column, we serialize an array of "Attributo" object, so typeof(T) is Attributo[]:
The "Attributo" definition is like the following:
public class Attributo
{
public virtual String Nome { get; set; }
public virtual String Valore { get; set; }
public virtual String Tipologia { get; set; }
}
- Deserialization to read the actual values:
XMLUtilities.Deserialize<Attributo[]>(value));
Serialization to store the values in the column (for each row..):
XMLUtilities.Serialize(attributes.ToArray());
And this is the helper class, which makes use of the XmlSerializer object:
public static class XMLUtilities
{
public static T Deserialize<T>(String xmlString)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (TextReader reader = new StringReader(xmlString))
{
return (T) serializer.Deserialize(reader);
}
}
public static String Serialize<T>(T xmlObject)
{
MemoryStream stream = new MemoryStream();
XmlSerializer serializer = new XmlSerializer(typeof(T));
XmlSerializerNamespaces xmlnsEmpty = new XmlSerializerNamespaces();
xmlnsEmpty.Add("", "");
serializer.Serialize(stream, xmlObject, xmlnsEmpty);
stream.Seek(0, SeekOrigin.Begin);
using (StreamReader reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
Now, the problem with this technique is that it wastes a lot of space for markup characters. This is an example string which is stored on the db:
<?xml version="1.0"?>
<ArrayOfAttributo>
<Attributo>
<Nome>Leakage_test_Time_prg1_p1</Nome>
<Valore>4</Valore>
<Tipologia>Single</Tipologia>
</Attributo>
<Attributo>
<Nome>Leakage_air_Volume_p1</Nome>
<Valore>14</Valore>
<Tipologia>Single</Tipologia>
</Attributo>
</ArrayOfAttributo>
So, we've found a more concise way of serializing these Attributo[], which produces this kind of output:
<ArrayOfA xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<A>
<N>Leakage_test_Time_prg1_p1</N>
<V>4</V>
<T>Single</T>
</A>
<A>
<N>Leakage_air_Volume_p1</N>
<V>14</V>
<T>Single</T>
</A>
</ArrayOfA>
then, to preserve backwards compatibility, which is the core issue we have implemented the following logic:
During serialization:
we always serialize in the new, more concise fashion
During deserialization:
we check whether the string starts with:
<?xml version="1.0"?>
or not. If that is the case, this is an old entry so we deserialize it in the old way. Otherwise, we deserialize using the new format.
We achieved that by decorating "Attributo" this way:
[DataContract(Name = "A", Namespace= "")]
public class Attributo
{
[DataMember(Name = "N")]
public virtual String Nome { get; set; }
[DataMember(Name = "V")]
public virtual String Valore { get; set; }
[DataMember(Name = "T")]
public virtual String Tipologia { get; set; }
}
and by performing the following changes to our serialization/deserialization methods, which now, for the new serialization technique, rely on DataContractSerializer object:
public static T Deserialize<T>(String xmlString)
{
//let's see if it's an old-style entry...
if (xmlString.StartsWith("<?xml version=\"1.0\"?>\r\n<ArrayOfAttributo>"))
{
try
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (TextReader reader = new StringReader(xmlString))
{
return (T)serializer.Deserialize(reader);
}
}
catch { }
}
//..then it must be a new-style one
DataContractSerializer ser = new DataContractSerializer(typeof(T));
using (Stream s = _streamFromString(xmlString))
{
return (T) ser.ReadObject(s);
}
}
public static String Serialize<T>(T xmlObject)
{
MemoryStream stream1 = new MemoryStream();
DataContractSerializer ser = new DataContractSerializer(typeof(T));
ser.WriteObject(stream1, xmlObject);
stream1.Position = 0;
StreamReader sr = new StreamReader(stream1);
string xmlString = sr.ReadToEnd();
return xmlString;
}
private static Stream _streamFromString(string s)
{
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
Everything seems to be working with this approach but we want to assess every possible risk before proceeding any further. Is this safe to use in production?
One more thing to keep in mind, while deserializing the older entry :
deserialize old-entry in old-style
serialize the old-entry in new-style
save the new-style-serialized-old-entry, and delete the old-style-serialized-old-entry.
You're good to go.

Binary Serialization/Deserialization with dictionaries and others class members

I need to serialize/deserialize a class with subclasses in which a member is a dictionary. Another member is a class storing passwords so it can't be XML but binary.
Have found that very nice solution which might be easy and issue-free.
http://theburningmonk.com/2010/05/net-tips-xml-serialize-or-deserialize-dictionary-in-csharp/
But this example (and all the others I have found) have an xml output which is not the right solution for my problem.
So the question is: "how to make datacontract serialization with binary and not xml output?"
Thanx in advance
---ADD---
Even using a filestream with that class:
[DataContract]
public class MyClass
{
// need a parameterless constructor for serialization
public MyClass()
{
MyDictionary = new Dictionary<string, string>();
}
[DataMember]
public Dictionary<string, string> MyDictionary { get; set; }
public int aaaa = 3;
}
and doing
MyClass theclass = new MyClass();
var serializer = new DataContractSerializer(typeof(MyClass));
bool append = true;
using (Stream fileStream = File.Open("aaa.bin", append ? FileMode.Append : FileMode.Create))
{
serializer.WriteObject(fileStream, theclass);
}
the output is XML.
Like Preston Guillot already mentioned in his comment. The DataContractSerializer.WriteObject also accepts a binary writer. Here is an example just for the sake of completeness.
MyClass theClass = new MyClass();
var serializer = new DataContractSerializer(typeof(MyClass));
bool append = true;
using (Stream fileStream = File.Open("aaa.bin", append ? FileMode.Append : FileMode.Create))
{
XmlDictionaryWriter binaryWriter = XmlDictionaryWriter.CreateBinaryWriter(fileStream);
serializer.WriteObject(binaryWriter, theClass);
binaryWriter.Flush();
}

Unable to make ShouldSerialize pattern work with XmlSerializer

I have been able to use ShouldSerializeProperty pattern with XmlSerializer to ignore a property of type ICollection<>. Below is the sample code. It works if I use XmlIgnore on ListOfTestClassB property but I want to achieve the same functionality utilizing ShouldSerialize pattern. If I run the code below I get the 'System.InvalidOperationException' saying:
Cannot serialize member ConsoleApplication3.TestClassA.ListOfTestClassB of type System.Collections.Generic.ICollection`1[[ConsoleApplication3.TestClassB, ConsoleApplication3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] because it is an interface.
Can anyone highlight what am I missing?
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication3
{
[Serializable]
public class TestClassA
{
public int A { get; set; }
public int B { get; set; }
public string C { get; set; }
public TestClassB TestClass { get; set; }
public virtual ICollection<TestClassB> ListOfTestClassB { get; set; }
public bool ShouldSerializeListOfTestClassB()
{
return false;
}
}
[Serializable]
public class TestClassB
{
public int Int32 { get; set; }
public string String { get; set; }
}
class Program
{
static object GetObject()
{
return new TestClassA { A = 1, B = 2, C = "test class a", TestClass = new TestClassB { Int32 = 11, String = "test class b"} };
}
static void Main(string[] args)
{
var result = new StringBuilder();
var entity = GetObject();
var ser = new XmlSerializer(entity.GetType());
var settings = new XmlWriterSettings { OmitXmlDeclaration = true };
using (var stream = new MemoryStream())
{
// make a copy of the entity - we do not want to serialize ZIP file !
var formatter = new BinaryFormatter();
formatter.Serialize(stream, entity);
stream.Position = 0;
entity = formatter.Deserialize(stream);
}
// serialize object
ser.Serialize(XmlWriter.Create(result, settings), entity);
Console.WriteLine(result.ToString());
Console.ReadLine();
}
}
}
This is where the check happens: http://referencesource.microsoft.com/#System.Xml/System/Xml/Serialization/Models.cs,249.
As you can see, it doesn't call the ShouldSerialize* method until after it's already identified fields/properties to serialize. So, your ListOfTestClassB has to be serializable, or it must be decorated with [XmlIgnore]. In order to be serializable your property must be of a concrete type that has the [Serializable] attribute applied.
There's a workaround if you can't modify the class. One of the overloads of the XmlSerializer.Serialize(...) method accepts an overrides object. I've created a simple example below:
[Serializable]
public class Foo
{
public IList<int> Numbers { get; set; }
public string TheNumber { get; set; }
}
class Program
{
private static void Main(string[] args)
{
var attributes = new XmlAttributes
{
XmlIgnore = true
};
var overrides = new XmlAttributeOverrides();
overrides.Add(typeof(Foo), "Numbers", attributes);
var serializer = new XmlSerializer(typeof(Foo), overrides);
// the rest of this is for demo purposes.
// the code above is whats important
//
using (var ms = new MemoryStream())
using (var reader = new StreamReader(ms))
{
serializer.Serialize(ms, new Foo() { TheNumber = "5" });
ms.Flush();
ms.Seek(0, SeekOrigin.Begin);
Debug.WriteLine(reader.ReadToEnd());
}
}
}
This generates:
<?xml version="1.0"?>
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TheNumber>5</TheNumber>
</Foo>
As you can see, I'm dynamically adding the XmlIgnore attribute to my Numbers element, and the serializer consequently ignores it. :) You should have no trouble adapting this to your own code.
Note: As noted by dbc, it's important to cache this serializer and re-use it, otherwise you're going to have a lot of memory leaks. You can keep a static reference to it, or use a hashtable to store different serializers for different types.
To increase performance, the XML serialization infrastructure dynamically generates assemblies to serialize and deserialize specified types. The infrastructure finds and reuses those assemblies. This behavior occurs only when using the following constructors:
XmlSerializer.XmlSerializer(Type)
XmlSerializer.XmlSerializer(Type, String)
If you use any of the other constructors, multiple versions of the same assembly are generated and never unloaded, which results in a memory leak and poor performance. The easiest solution is to use one of the previously mentioned two constructors. Otherwise, you must cache the assemblies in a Hashtable, as shown in the following example.
If you use XmlIgnore, then it will not care about that property at all. If you use ShouldSerialize, it doesn't know until runtime whether it should serialize that type, so it must be able to. In this case, the type you're trying to serialize must be a concrete class. Try using List<TestClassB>.

XMLSerialization: The type of the argument object 'Sw' is not primitive

I'm trying to serialize an object to an XML file, but am getting the above error.
The problem seems to be with objects that contain a list of a base class but is populated by objects derived from the base class.
Example code is as follows:
public class myObject
{
public myObject()
{
this.list.Add(new Sw());
}
public List<Units> list = new List<Units>();
}
public class Units
{
public Units()
{
}
}
public class Sw : Units
{
public Sw();
{
}
public void main()
{
myObject myObject = new myObject();
XmlSerializer serializer = new XmlSerializer(typeof(myObject));
TextWriter textWriter = new StreamWriter ("file.xml");
serializer.Serialize (textWriter, myObject);
}
E.g. an object that contains only a List<Units> which is populated by derived objects which inherit from the Units class (Sw).
Sorry for not providing my actual code but the objects involved are quite complex and this seems to be the only part of the object which wont successfully be serialized - and only when the list contains the derived classes.
How can I correctly serialize a class like this?
Mark Units class with XmlInclude attribute passing your derived class as parameter:
[XmlInclude(typeof(Sw))]
public class Units
{
public Units()
{
}
}
To Serialize an object to XML. you can use the following code
public String SerializeResponse(SW sw)
{
try
{
String XmlizedString = null;
XmlSerializer xs = new XmlSerializer(typeof(SW));
//create an instance of the MemoryStream class since we intend to keep the XML string
//in memory instead of saving it to a file.
MemoryStream memoryStream = new MemoryStream();
//XmlTextWriter - fast, non-cached, forward-only way of generating streams or files
//containing XML data
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
//Serialize emp in the xmlTextWriter
xs.Serialize(xmlTextWriter, sw);
//Get the BaseStream of the xmlTextWriter in the Memory Stream
memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
//Convert to array
XmlizedString = UTF8ByteArrayToString(memoryStream.ToArray());
return XmlizedString;
}
catch (Exception ex)
{
throw;
}
}
The method will return an XML String and to make the above function you need to import the following libraries:
using System.Xml;
using System.Xml.Serialization;
using System.IO;

Categories