I am trying to serialize image into XElement and afterwards deserialize it for further use.
I am using this method to serialize:
public XElement subElement = new XElement("Element");
private void Serialize(System.Windows.Forms.Button button) {
if (button.Image != null) {
var bf = new BinaryFormatter();
var ms = new MemoryStream();
bf.Serialize(ms, button.Image);
var textWriter = new StringWriter();
var writer = new XmlTextWriter(textWriter);
byte[] imageBytes = ms.ToArray();
writer.WriteBase64(imageBytes, 0, imageBytes.Length);
subElement.Add(new XAttribute("Image", imageBytes));
}
}
But I can't figure out how to deserialize. I tried something like this:
private void Deserialize(XElement element) {
if (element.Attribute("Image") != null) {
//tried XmlReader reader = XmlReader.Create(new StringReader(element.Attribute("Image").Value));
//but reader is empty
//when I try: XmlReader reader = XmlReader.Create(element.Attribute("Image").Value);
//exception is thrown because XmlReader expects path, not element
}
}
I basically only need to get byte array from XElement, later I know how to handle it.
Once you have your byte array you can do Convert.ToBase64String(byteArray). The result of that function (a string) is what goes in the XAttribute's value.
Then when it comes to reading, you'd just do byteArray = Convert.FromBase64String(element.Attribute("Image").Value)
This should prevent the issues with saving the string within the XML file.
Don't use serialization, simple save/load it from memory stream.
Related
I am trying to serialize a simple object (5 string properties) into XML to save to a DB Image field. Then I need to DeSerialize it back into a string later in the program.
However, I am getting some errors - caused by the XML being saved thinking it is in UTF-16 - however, when I load it from the DB back into a string - it thinks it is a UTF 8 String.
The error I get is
InnerException {"There is no Unicode byte order mark. Cannot switch to Unicode."} System.Exception {System.Xml.XmlException}
-- Message "There is an error in XML document (0, 0)." string
Is this happening because of the two different ways I save and load the string to/from the DB? On the save I am using a StringBuilder - but on the load from DB I am using just a String.
Thoughts?
Serialize and Save to DB
// Now Save the OBject XML to the Query Tables
var serializer = new XmlSerializer(ExportConfig.GetType());
StringBuilder StringResult = new StringBuilder();
using (var writer = XmlWriter.Create(StringResult))
{
serializer.Serialize(writer, ExportConfig);
}
//MessageBox.Show("XML : " + StringResult);
// Now Save to the Query
try
{
string UpdateSQL = "Update ZQryRpt "
+ " Set ExportConfig = " + TAGlobal.QuotedStr(StringResult.ToString())
+ " where QryId = " + TAGlobal.QuotedStr(((DataRowView)bindingSource_zQryRpt.Current).Row["QryID"].ToString())
;
ExecNonSelectSQL(UpdateSQL, uniConnection_Config);
}
catch (Exception Error)
{
MessageBox.Show("Error Setting ExportConfig: " + Error.Message);
}
Load from DB And Deserialize
byte[] binaryData = (byte[])((DataRowView)bindingSource_zQryRpt.Current).Row["ExportConfig"];
string XMLStored = System.Text.Encoding.UTF8.GetString(binaryData, 0, binaryData.Length);
if (XMLStored.Length > 0)
{
IIDExportObject ExportConfig = new IIDExportObject();
var serializer = new XmlSerializer(ExportConfig.GetType());
//StringBuilder StringResult = new StringBuilder(XMLStored);
// Load the XML from the Query into the StringBuilder
// Now we need to build a Stream from the String to use in the XMLReader
byte[] byteArray = Encoding.UTF8.GetBytes(XMLStored);
MemoryStream stream = new MemoryStream(byteArray);
using (var reader = XmlReader.Create(stream))
{
ExportConfig = (IIDExportObject)serializer.Deserialize(reader);
}
}
John - thank you very much for the comment! It allowed me to complete the code and find a solution.
As you noted - using a stream reader was the solution - but I could not read the first line because there was only one 'line' in my string. However, I could use the line
using (StreamReader sr = new StreamReader(stream, false))
Which allows me to read the stream and ignore the "Byte Order Mark Detection" set to false.
string XMLStored = MainFormRef.GetExportConfigForCurrentQuery();
if (XMLStored.Length > 0)
{
IIDExportObject ExportConfig = new IIDExportObject();
try
{
var serializer = new XmlSerializer(ExportConfig.GetType());
// Now we need to build a Stream from the String to use in the XMLReader
byte[] byteArray = Encoding.UTF8.GetBytes(XMLStored);
MemoryStream stream = new MemoryStream(byteArray);
// Now we need to use a StreamReader to get around UTF8 vs UTF16 issues
// A little cumbersome - but it works
using (StreamReader sr = new StreamReader(stream, false))
{
using (var reader = XmlReader.Create(sr))
{
ExportConfig = (IIDExportObject)serializer.Deserialize(reader);
}
}
}
catch
{
}
I am not sure this is the best solution - but it works. I will be curious to see if anyone else has a better way of dealing with this.
Thanks to G Bradley, I took his answer and generalized it a bit to make it a bit easier to call.
public static string SerializeToXmlString<T>(T objectToSerialize)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = false;
settings.Encoding = Encoding.UTF8;
StringBuilder builder = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
serializer.Serialize(writer, objectToSerialize);
}
return builder.ToString();
}
public static T DeserializeFromXmlString<T>(string xmlString)
{
if (string.IsNullOrWhiteSpace(xmlString))
return default;
var serializer = new XmlSerializer(typeof(T));
byte[] byteArray = Encoding.UTF8.GetBytes(xmlString);
MemoryStream stream = new MemoryStream(byteArray);
using (StreamReader sr = new StreamReader(stream, false))
{
using (var reader = XmlReader.Create(sr))
{
return (T)serializer.Deserialize(reader);
}
}
}
i want to save a Stroke in a memorystream
for this purpose is used BinaryFormatter but when i try to serialize Stroke i get a error that i can't serialize Stroke
is there any way to save a Stroke in a memorystream or serialize Stroke?
here is one part of my code
int size = inkCanvas1.Strokes.Count();
IFormatter formatter = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
if (size != 0)
{
try
{
formatter.Serialize(stream, inkCanvas1.Strokes[size - 1]);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
thanks.
The reason that this doesn't work is that StrokeCollection doesn't have the SerializableAttribute applied.
But you can use the StrokeCollection.Save method for this.
var ms = new MemoryStream();
using (ms)
{
StrokeCollection sc = ...;
sc.Save(ms);
ms.Position = 0;
}
And then when you need the StrokeCollection again, you can use the constructor that accepts a Stream.
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream m = new MemoryStream())
{
formatter.Serialize(m, list);
StreamReader sr = new StreamReader(m);
HiddenField1.Value = sr.ReadToEnd();
}
i'm getting a blank value for HiddenField1.Value. Not sure what I'm doing is even possible? list is definitely populated (is a List<T>)
Depending on what you want to achieve... One option is to show content of the binary stream as Base64 string:
var memoryStream = new MemoryStream();
using(memoryStream)
{
formatter.Serialize(memoryStream, list);
}
HiddenField1.Value = Convert.ToBase64String(memoryStream.ToArray());
Change it to:
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream m = new MemoryStream())
{
formatter.Serialize(m, list);
m.Position = 0;
StreamReader sr = new StreamReader(m);
HiddenField1.Value = sr.ReadToEnd();
}
You need to reset the position of the stream back to the beginning before reading it. Also, you shouldn't use StreamReader to convert a binary stream like this to text, because it will break in unexpected ways. If you want the results in a text-like format, use Convert.ToBase64String as in #Alexei's answer.
I've an application that search XML over the network (using TcpClient), these XML have various encoding (one site in UTF8, other in Windows-1252). I would like encode all of these XML in UTF-8 (always) to be sure I'm clean.
How can I do the conversion from the NetworkStream to an XElement encoding correctly all data?
I've this :
NetworkStream _clientStream = /* ... */;
MemoryStream _responseBytes = new MemoryStream();
// serverEncoding -> Xml Encoding I get from server
// _UTF8Encoder -> Local encoder (always UTF8)
try
{
_clientStream.CopyTo(_responseBytes);
if (serverEncoding != _UTF8Encoder)
{
MemoryStream encodedStream = new MemoryStream();
string line = null;
using (StreamReader reader = new StreamReader(_responseBytes))
{
using (StreamWriter writer = new StreamWriter(encodedStream))
{
while ((line = reader.ReadLine()) != null)
{
writer.WriteLine(
Encoding.Convert(serverEncoding, _UTF8Encoder, serverEncoding.GetBytes(line))
);
}
}
}
_responseBytes = encodedStream;
}
_responseBytes.Position = 0;
using (XmlReader reader = XmlReader.Create(_responseBytes))
{
xmlResult = XElement.Load(reader, LoadOptions.PreserveWhitespace);
}
}
catch (Exception ex)
{ }
Have you a better solution (and by ignoring all '\0' ?).
Edit
This works :
byte[] b = _clientStream.ReadToEnd();
var text = _UTF8Encoder.GetString(b, 0, b.Length);
xmlResult = XElement.Parse(text, LoadOptions.PreserveWhitespace);
But this not :
using (var reader = new StreamReader(_clientStream, false))
xmlResult = XElement.Load(reader, LoadOptions.PreserveWhitespace);
I don't understand why ...
You can simply create a StreamReader around the NetworkStream, passing the encoding of the stream, then pass it to XElement.Load:
XElement elem
using(var reader = new StreamReader = new StreamReader(_clientStream, serverEncoding))
elem = XElement.Load(reader);
There is no point in manually transcoding it to a different encoding.
I want to make a binary serialize of an object and the result to save it in a database.
Person person = new Person();
person.Name = "something";
MemoryStream memorystream = new MemoryStream();
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(memorystream, person);
How can I transform memorystream in a string type to be saved in database, and after this to be able to deserialize the object?
What you're really asking for is a safe way of representing arbitrary binary data as text and then converting it back again. The fact that it stores a serialized object is irrelevant.
The answer is almost to use Base 64 (e.g. Convert.ToBase64String and Convert.FromBase64String). Do not use Encoding.UTF8.GetString or anything similar - your binary data is not encoded text data, and shouldn't be treated as such.
However, does your database not have a data type for binary data? Check for BLOB, IMAGE and BINARY types...
Here's the sample. TData must be marked [Serializable] and all fields type also.
private static TData DeserializeFromString<TData>(string settings)
{
byte[] b = Convert.FromBase64String(settings);
using (var stream = new MemoryStream(b))
{
var formatter = new BinaryFormatter();
stream.Seek(0, SeekOrigin.Begin);
return (TData)formatter.Deserialize(stream);
}
}
private static string SerializeToString<TData>(TData settings)
{
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, settings);
stream.Flush();
stream.Position = 0;
return Convert.ToBase64String(stream.ToArray());
}
}
//-------write to database-------------------------
Person person = new Person();
person.name = "Firstnm Lastnm";
MemoryStream memorystream = new MemoryStream();
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(memorystream, person);
byte[] yourBytesToDb = memorystream.ToArray();
//here you write yourBytesToDb to database
//----------read from database---------------------
//here you read from database binary data into yourBytesFromDb
MemoryStream memorystreamd = new MemoryStream(yourBytesFromDb);
BinaryFormatter bfd = new BinaryFormatter();
Person deserializedperson = bfd.Deserialize(memorystreamd) as Person;
I used something like this
MemoryStream memoryStream = new MemoryStream();
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, Person);
memoryStream.Flush();
memoryStream.Position = 0;
string value = Convert.ToBase64String(memoryStream.ToArray());
Basically, don't save the data as string to the database, there are blob fields available to store binary data.
If you really need to have the data as string, you'll need to convert your byte[] to a string using base64 encoding, and to grab the byte[] from a string use decoding.
Have you not looked into converting the memorystream into a base64hex string to be put into the database?
byte[] mStream = memorystream.ToArray();
string sConvertdHex = System.Convert.ToBase64String(mStream)
Then you can dump the contents sConvertdHex to the database. To deserialize it you need to do the reverse
byte[] mData = System.Convert.FromBase64String(...)
then deserialize mData back to your object.