I am developing a WCF service which will be consumed by multiple different client applications. In order to make one functionality work, the server needs to read an XML file into a C# DataContract which is then passed on to the concerned client. As far as I understand from the MSDN website, this is possible but I couldn't find any complete examples. In particular, the website talks about a 'stream' parameter which I don't quite get yet.
My data contract has one property field which is a list of another data contract which has multiple simple property fields.
e.g.
[DataContract]
public class MyClass1 {
[DataMember]
public string name;
[DataMember]
public int age;
}
[DataContract]
public class MyClass2 {
[DataMember]
public List<MyClass1> myClass1List;
}
My classes look something like this.
Here is an example
MyClass1 obj = new MyClass1();
DataContractSerializer dcs = new DataContractSerializer(typeof(MyClass1));
using (Stream stream = new FileStream(#"C:\tmp\file.xml", FileMode.Create, FileAccess.Write))
{
using (XmlDictionaryWriter writer =
XmlDictionaryWriter.CreateTextWriter(stream, Encoding.UTF8))
{
writer.WriteStartDocument();
dcs.WriteObject(writer, obj);
}
}
Books b = new Books();
DataContractSerializer dcs = new DataContractSerializer(typeof(Books));
try
{
Stream fs = new FileStream(#"C:\Users\temelm\Desktop\XmlFile.xml", FileMode.Create, FileAccess.Write);
XmlDictionaryWriter xdw = XmlDictionaryWriter.CreateTextWriter(fs, Encoding.UTF8);
xdw.WriteStartDocument();
dcs.WriteObject(xdw, b);
xdw.Close();
fs.Flush();
fs.Close();
}
catch (Exception e)
{
s += e.Message + "\n";
}
This can be useful for you. When you need XElement. For instance when you going append node to XDocument or replece XElement of this document.
private XElement objectToXElement(SomeContractType obj)
{
DataContractSerializer dcs = new DataContractSerializer(typeof(SomeContractType);
var ms = new MemoryStream();
var xw = XmlWriter.Create(ms);
dcs.WriteObject(xw, obj);
xw.Flush();
xw.Close();
ms.Position = 0;
XElement xe = XElement.Load(ms);
return xe;
}
There is the NetDataContractSerializer which solves a whole bunch of problems when using WCF.
See here MSDN NetDataContractSerializer
It is typically used for wrapping all kinds of objects and pass it over WCF.
You can use it for wrapping objects into a byte[] and transport it over WCF,
on the serverside, you can easily Deserialize the objects and do whatever
you want with them.
Here is a discussion on how to use this Serializer correctly:
MSDN Social
Code snippets are provided there also!
Related
BinaryFormatter is able to handle serialization simply:
private byte[] TokenToBytes(SessionSecurityToken token)
{
if (token == null)
{
return null;
}
using (var memoryStream = new MemoryStream())
{
var binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, token);
return memoryStream.ToArray();
}
}
When I tried replacing BinaryFormatter with protobuf-net:
using (var memoryStream = new MemoryStream())
{
Serializer.Serialize(memoryStream, token);
return memoryStream.ToArray();
}
I get the following exception:
Type is not expected, and no contract can be inferred:
System.IdentityModel.Tokens.SessionSecurityToken
I tried adding:
RuntimeTypeModel.Default.Add(typeof(SessionSecurityToken), true);
Which gets past the exception but I now get a zero byte array.
How do I properly configure protobuf-net to serialize a SessionSecurityToken?
On the flipside, SessionSecurityToken does not have a parameterless constructor.
using (var memoryStream = new MemoryStream(tokenAsBytes))
{
return Serializer.Deserialize<SessionSecurityToken>(memoryStream);
}
Throws a ProtoException:
No parameterless constructor found for SessionSecurityToken
BinaryFormatter is able to do it without any fuss:
using (var memoryStream = new MemoryStream(bytes))
{
var binaryFormatter = new BinaryFormatter();
return (SessionSecurityToken)binaryFormatter.Deserialize(memoryStream);
}
How do I properly configure protobuf-net to deserialize a SessionSecurityToken?
protobuf-net does not claim to be able to serialize every single type; indeed, you would have great difficulty serializing that via most serializers (XmlSerializer, any of the json serializers, DataContractSerializer, etc). BinaryFormatter is in a different category of serializers - and in this particular case, implements custom serialization via ISerializable.GetObjectData(SerializationInfo, StreamingContext).
The constructor thing is a red herring; actually, protobuf-net can bypass constructors completely, and in this particular scenario BinaryFormatter is using a custom serialization constructor via .ctor(SerializationInfo, StreamingContext).
For simple cases, protobuf-net can be configured via attributes or runtime options; for more complex scenarios, surrogates can be used to map between representations - however, in this case I would suggest (looking at the implementation of SessionSecurityToken) that this is more complex than you probably want to maintain.
I would step back a step or two here; most serializers are designed to work with data, not implementation - and work great with DTOs etc. SessionSecurityToken is very much not a DTO, and there is no simple way of switching between them. My strong suggestion here would be: serialize what this represents, not what it is. However, if this is part of an existing complex model and is really really hard to separate out, you could switch back to BinaryFormatter for those bits. I haven't tested this, but consider:
RuntimeTypeModel.Default.Add(typeof(SessionSecurityToken), false)
.SetSurrogate(typeof(BinaryFormatterSurrogate<SessionSecurityToken>));
With:
[ProtoContract]
public class BinaryFormatterSurrogate<T>
{
[ProtoMember(1)]
public byte[] Raw { get; set; }
public static explicit operator T(BinaryFormatterSurrogate<T> value)
{
if(value==null || value.Raw == null) return default(T);
using(var ms = new MemoryStream(value.Raw))
{
return (T)new BinaryFormatter().Deserialize(ms);
}
}
public static explicit operator BinaryFormatterSurrogate<T>(T value)
{
object obj = value;
if (obj == null) return null;
using (var ms = new MemoryStream())
{
new BinaryFormatter().Serialize(ms, obj);
return new BinaryFormatterSurrogate<T> { Raw = ms.ToArray() };
}
}
}
Keep in mind that this simply embeds the output of one serializer as raw data inside another. Fortunately protobuf-net is happy talking binary, so this won't add any noticeable overhead (just the header and length-prefix for the blob) - but it also won't do anything particularly smart or clever with the SessionSecurityToken instances. If this is the only thing you are serializing, it really isn't worth it. If this is just one ugly bump in a larger DTO model, where most of it can serialize nicely - then it might get the job done for you.
using System;
public class clsPerson
{
public string FirstName;
public string MI;
public string LastName;
}
class class1
{
static void Main(string[] args)
{
clsPerson p=new clsPerson();
p.FirstName = "Jeff";
p.MI = "A";
p.LastName = "Price";
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(p.GetType());
x.Serialize(Console.Out, p);
Console.WriteLine();
Console.ReadLine();
}
}
taken from http://support.microsoft.com/kb/815813
1)
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(p.GetType());
What does this line do? what is GetType()?
2) how do I get the encoding to
<?xml version="1.0" encoding="utf-8"?>
< clsPerson xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
instead of
<?xml version="1.0" encoding="IBM437"?>
<clsPerson xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3
.org/2001/XMLSchema">
or not include the encoding type at all?
If you pass the serializer an XmlWriter, you can control some parameters like encoding, whether to omit the declaration (eg for a fragment), etc.
This is not meant to be a definitive guide, but an alternative so you can see what's going on, and something that isn't just going to console first.
Note also, if you create your XmlWriter with a StringBuilder instead of a MemoryStream, your xml will ignore your Encoding and come out as utf-16 encoded. See the blog post writing xml with utf8 encoding for more information.
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings
{
Indent = true,
OmitXmlDeclaration = false,
Encoding = Encoding.UTF8
};
using (MemoryStream memoryStream = new MemoryStream() )
using (XmlWriter xmlWriter = XmlWriter.Create(memoryStream, xmlWriterSettings))
{
var x = new System.Xml.Serialization.XmlSerializer(p.GetType());
x.Serialize(xmlWriter, p);
// we just output back to the console for this demo.
memoryStream.Position = 0; // rewind the stream before reading back.
using( StreamReader sr = new StreamReader(memoryStream))
{
Console.WriteLine(sr.ReadToEnd());
} // note memory stream disposed by StreamReaders Dispose()
}
1) The GetType() function returns a Type object representing the type of your object, in this case the class clsPerson. You could also use typeof(clsPerson) and get the same result. That line creates an XmlSerializer object for your particular class.
2) If you want to change the encoding, I believe there is an override of the Serialize() function that lets you specify that. See MSDN for details. You may have to create an XmlWriter object to use it though, details for that are also on MSDN:
XmlWriter writer = XmlWriter.Create(Console.Out, settings);
You can also set the encoding in the XmlWriter, the XmlWriterSettings object has an Encoding property.
I took the solution offered by #robert-paulson here for a similar thing I was trying to do and get the string of an XmlSchema. By default it would return as utf-16. However as mentioned the solution here suffers from a Stream Closed Read error. So I tool the liberty of posting the refactor as an extension method with the tweek mentioned by #Liam to move the using block.
public static string ToXmlString(this XmlSchema xsd)
{
var xmlWriterSettings = new XmlWriterSettings
{
Indent = true,
OmitXmlDeclaration = false,
Encoding = Encoding.UTF8
};
using (var memoryStream = new MemoryStream())
{
using (var xmlWriter = XmlWriter.Create(memoryStream, xmlWriterSettings))
{
xsd.Write(xmlWriter);
}
memoryStream.Position = 0;
using (var sr = new StreamReader(memoryStream))
{
return sr.ReadToEnd();
}
}
}
1) This creates a XmlSerializer for the class clsPerson.
2) encoding is IBM437 because that is the form for the Console.Out stream.
PS: Hungarian notation is not preferred in C#; just name you class Person.
I have a serialization utility that serializes an object to an XDocument. It works quite well :
public static class SerializationUtil
{
public static T Deserialize<T>(XDocument doc)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (var reader = doc.Root.CreateReader())
{
return (T)xmlSerializer.Deserialize(reader);
}
}
public static XDocument Serialize<T>(T value)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
XDocument doc = new XDocument(z);
using (var writer = doc.CreateWriter())
{
xmlSerializer.Serialize(writer, value);
}
return doc;
}
Been using it quite happily and suddenly I get :
There was an error generating the XML document.
The inner exception is :
This XmlWriter does not support base64 encoded data.
Turns out that the XDocument.CreateWriter() instance method gives you a writer of type System.Xml.XmlWellFormedWriter, and that that writer can't write base64 encoded data (my object contains a byte[]).
MSDN doesn't even seem to mention this class - but I can't seem to create any other type of writer from XDocument.
I could just serialize to a string, but i was trying to be clever and avoid using any hacks. Any way to serialize to XDocument when base64 is needed for certain fields.
According to the docs, there's no allowance for bytes. A surrogate base64 encoded string property is probably your best bet (is it a hack if its by design?).
I want to be able to send a XmlSerializer class (which is generated obvious in remote C# application) over a WebService that will then deserialize it into a class. (I didnt know it its possible either)
My class is:
SystemInfo
I'm serializing it this way:
XmlSerializer mySerializer = new XmlSerializer(typeof(SystemInfo));
StreamWriter myWriter = new StreamWriter(textBox1.Text);
mySerializer.Serialize(myWriter, sysinfo);
How can i build the WebService?
[WebMethod]
public void Reports(XmlSerializer xmlSerializer)
{
.
.
.
}
Can anyone help me out?
Regards
First I assume you want to pass arbitrary types to a single web method, where the types are shared by the client and the server.
There is not much point in sending the XmlSerializer, it only has the logic to serialize/deserialize. It does not have the actual data, that is either read/written to a stream. What you should do is pass either a string or and XmlNode.
The caller of the web service can then a client side instance of XmlSerializer and serialize the object to a string, then call the web method passing the string as an argument. The web method it self can then create an instance of a XmlSerializer and deserialize the string back into an object. Of course to create the server size instance of the serializer you will need to know the root type to create the serializer for, you can pass this as a type name and use Type.GetType() to get the correct type to pass to the XmlSerializer.
If you know upfront which types you are going to be passing then you could also declare your web method more strongly typed and explicitly create methods for the types you expect to recieve.
If the wire format is not too much of a concern, you could also user SoapFormatter or a BinaryFormatter to handle the serialization/deserialization. In the later case of the BinaryFormatter you would declare your web method to take a byte[] argument, the advantage of these formatters (serializers) is that they do not need additional info on the type when you create the instance of the formatter, but they can be slower than an XmlSerializer
Update: Added some simple examples (Untested)
Example using an XmlSerializer, here you will need to pass the type name from the client side, so I made it an additional argument.
[WebMethod]
public void Reports(string xml, string typeName)
{
XmlSerializer xs = new XmlSerializer(Type.GetType(typeName));
object obj = xs.Deserialize(new StringReader(xml));
// use the deserialize object
}
Example using a BinaryFormatter, no type name needed but you the class types will need to be serializable
[WebMethod]
public void Reports(byte[] data)
{
BinaryFormatter bf = new BinaryFormatter();
object obj = bf.Deserialize(new MemoryStream(data));
// use the deserialized object
}
On the client side you would use something like the following to serialize using the BinaryFormatter.
// initialize the SystemInfo instance that you want to pass to the server
SystemInfo si = new SystemInfo() { SystemName = "My System" };
// Serialize to a memory stream
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, si);
// Call the service, passing the array from the memory stream
ws.Reports(ms.ToArray());
Chris, thanks for helping me out. It was a major step forward.
I solved the problem sending the xml string:
SystemInfo sysinfo = new SystemInfo();
sysinfo.RUN();
XmlSerializer mySerializer = new XmlSerializer(typeof(SystemInfo));
MemoryStream memStream = new MemoryStream();
mySerializer.Serialize(memStream, sysinfo);
memStream.Seek(0, System.IO.SeekOrigin.Begin);
XmlDocument doc = new XmlDocument();
doc.Load(memStream);
memStream.Close();
localhost.WS_Agente dasdsa = new localhost.WS_Agente();
dasdsa.Reports(doc.InnerXml);
And the WebService:
[WebMethod]
public void Reports(string xml)
{
XmlSerializer mySerializer = new XmlSerializer(typeof(SystemInfo));
SystemInfo obj = (SystemInfo)mySerializer.Deserialize(new StringReader(xml));
}
Its working like a charm now :)
My question is: Can i improve the code?
Thanks
I just wrote this SerializationHelper class, but I can't believe this is necessary!
using System.IO;
using System.Xml.Serialization;
public static class SerializationHelper
{
public static string Serialize<T>(T obj)
{
var outStream = new StringWriter();
var ser = new XmlSerializer(typeof(T));
ser.Serialize(outStream, obj);
return outStream.ToString();
}
public static T Deserialize<T>(string serialized)
{
var inStream = new StringReader(serialized);
var ser = new XmlSerializer(typeof(T));
return (T)ser.Deserialize(inStream);
}
}
And it's used like this:
var serialized = SerializationHelper.Serialize(myObj);
and:
var myObj = SerializationHelper.Deserialize<MyType>(serialized)
Am I missing something in the .NET framework? This is not rocket science!
In actual fact, the bits where you call the .NET API are these:
var ser = new XmlSerializer(typeof(T));
ser.Serialize(outStream, obj);
var ser = new XmlSerializer(typeof(T));
var obj = (T) ser.Deserialize(inStream);
The rest of the code is your personal specialisation. I don't think that two lines of code is too much for calling an API. You could always condense them, e.g.
(new XmlSerializer(typeof(T))).Serialize(outStream, obj);
var obj = (T) (new XmlSerializer(typeof(T))).Deserialize(inStream);
Purely as an aside, I should point out that I regard storing XML data in string variables as a Code Smell. As soon as you take XML data out of its raw binary form (XDocument, XmlDocument, XPathDocument or any other type of DOM), you run up against encoding issues. What if a developer serialises an object to a string with encoding X, then writes the string to a disk file with encoding Y? Not very safe. Besides which, if encoding X is not UTF-16, how would you even represent the data in a .NET string?
It's useful if you are doing any real amount (>1) of serialization/deserialization within a project. This was the case for me one time, so I just put a similar class in a Utils library, along with other reusable functions.