Got a little bit of a problem. I have a program that builds an observable collection of Users. The User has a Firstname, Lastname, and Image. I can add the user to the observable collection, but I also want to save the collection and load it everytime I reopen the program.
My problem is that while its fairly easy to save a firstname and lastname, the writer can't write the image to the xml file. Is there any way around this?
Here's what I have so far:
the observable collection:
ObservableCollection<VendorClass> ProfileList = new ObservableCollection<VendorClass>();
the problematic writer:
XmlSerializer xs = new XmlSerializer(typeof(ObservableCollection<VendorClass>));
using (StreamWriter wr = new StreamWriter("vendors.xml")) //Data/customers.xml
{
xs.Serialize(wr, ProfileList);
}
Any ideas? And if there does exist a solution to write in an image, is there a viable way to read it out again?
XmlSerializer can't serialize or deserialize the WPF image types like BitmapImage etc. It is however able to (de)serialize byte arrays. So you may add a byte[] ImageBuffer property to your Person class, which contains the binary image data. You would then also set the XmlIgnore attribute on the Image property to suppress its (de)serialization, and set XmlElement("Image") on the ImageBuffer properties to (de)serialize it as <Image>...</Image>.
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
[XmlIgnore]
public BitmapSource Image { get; set; }
[XmlElement("Image")]
public byte[] ImageBuffer
{
get
{
byte[] imageBuffer = null;
if (Image != null)
{
using (var stream = new MemoryStream())
{
var encoder = new PngBitmapEncoder(); // or some other encoder
encoder.Frames.Add(BitmapFrame.Create(Image));
encoder.Save(stream);
imageBuffer = stream.ToArray();
}
}
return imageBuffer;
}
set
{
if (value == null)
{
Image = null;
}
else
{
using (var stream = new MemoryStream(value))
{
var decoder = BitmapDecoder.Create(stream,
BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
Image = decoder.Frames[0];
}
}
}
}
}
This approach has also been suggested for properties of type Bitmap in this answer.
You would base64 encode the image to convert it to a string and then write that into a CDATA section. See How do you serialize a string as CDATA using XmlSerializer?
Related
I have a class with a byte array as an attribute as binary
[System.Xml.Serialization.XmlAttributeAttribute(DataType="hexBinary")]
public byte[] aValue {
get {
return this.aValueField;
}
set {
this.aValueField= value;
}
}
The data itself...for aValue...has a String inside in the XML file I am attempting to deserialize in certain files
.
To deserialize I do this:
XmlSerializer xml = new XmlSerializer(typeof(Data));
using (Stream reader = new FileStream(file, FileMode.Open))
{
config = (Data)xml.Deserialize(reader);
}
The problem is, the data in the XML file, it has a String there not a byte[] (but other files do have a valid byte[] too). I cannot change the input file data nor can I change the attribute to a String, it has to be a byte[] for other files processed. Is there a way to do a custom conversion during this deserialization process somehow during via code for just this field, if the input is a String, to do a Custom Conversion to byte[] using logic? That way it doesn't exception and not get the class.
If you can differentiate between the string and the byte[] in code, you can add another property to handle the serialization, and make aValue XmlIgnore so it won't be deserialized but it will still get set inside bValue set.
private string bValueField;
[System.Xml.Serialization.XmlIgnore]
public byte[] aValue { get; set; }
[System.Xml.Serialization.XmlAttribute("aValue")]
public string bValue
{
get
{
return bValueField;
}
set
{
if (value.Contains("string identifier here")) // i.e. it's not a byte[]
{
aValue = new byte[] { };
bValueField = value;
}
else // it's a byte[]
{
var formatter = new BinaryFormatter();
using (var stream = new MemoryStream(Encoding.Unicode.GetBytes(value ?? "")))
{
aValue = (byte[])formatter.Deserialize(stream);
bValueField = "not a string";
}
}
}
}
I got 2 user-defined classes, called Event and Image, Event has a property stored a list of EventImage, called EventImages. And in Image class, there's a byte[] type property which store the byte[] of one Image file.
Here's the definitions of the 2 classes :
[Serializable]
public Class Event
{
public String Name {get; set;}
public DateTime EventTime { get; set;}
public List<Image> EventImages { get; set; }
...
}
[Serializable]
public Class Image
{
public DateTime ImageTime { get; set;}
public byte[] pData {get; set;}
...
}
Now my question is, I want to serialize my Event object to byte[], and I expect that the whole content of it will be serialize, too, but it seems that I failed.
Here's my code to do Serialization :
public static byte[] ObjectToByteArray(object obj)
{
if (obj == null)
{
return null;
}
else
{
BinaryFormatter bF = new BinaryFormatter();
using (MemoryStream mS = new MemoryStream())
{
bF.Serialize(mS, obj);
return mS.ToArray();
}
}
}
and here's the code for verification :
Console.WriteLine(ObjectToByteArray(Event));
Console.WriteLine(ObjectToByteArray(Event.EventImage));
Console.WriteLine(ObjectToByteArray(Event.EventImages.FirstOrDefault().pData));
and the results are(just assumption value) :
100 200 300
But I expect the result should be 600(100+200+300), 500(200+300) and 300.
So, I think my serialization doesn't really serialize the whole content, it just serialize properties with Basic Types, but without the nested objects, am I right?
I've searched lots of posts, and I found plenty of answers to similar questions mentioned "XML Serialization", but I'm not sure whether its helpful or not. Need I use that or is there any other better way? Thanks in advance!
Quickly made a test to check equality. It passes.
Serializes and deserializes correctly.
Conclusion, don't judge whether something is happening or not until you've tested it.
public static void Run()
{
var i = new Image
{
ImageTime = DateTime.UtcNow,
pData = Guid.NewGuid().ToByteArray()
};
var e = new Event
{
Name = Guid.NewGuid().ToString(),
EventTime = DateTime.UtcNow,
EventImages = new List<Image> {i}
};
var bytes = ObjectToByteArray(e);
var e2 = ObjectFromByteArray(bytes);
Console.WriteLine(e.Equals(e2));
}
public static byte[] ObjectToByteArray(object obj)
{
if (obj == null)
{
return null;
}
var bF = new BinaryFormatter();
using (var mS = new MemoryStream())
{
bF.Serialize(mS, obj);
return mS.ToArray();
}
}
public static object ObjectFromByteArray(byte[] bytes)
{
if (bytes == null)
{
return null;
}
var bF = new BinaryFormatter();
using (var mS = new MemoryStream(bytes))
{
return bF.Deserialize(mS);
}
}
I have been using some code to create MTOM by using code from MSDN.
It seems that there is an error and I cannot understand where the problem lies as one of the users on the forum pointed out that there is an error.
The file (JPEG) data get corrupted after a de-serialization. The complete code is listed below.
public class Post_7cb0ff86_5fe1_4266_afac_bcb91eaca5ec
{
[DataContract()]
public partial class TestAttachment
{
private byte[] fileField;
private string filenameField;
[DataMember()]
public byte[] File
{
get
{
return this.fileField;
}
set
{
this.fileField = value;
}
}
[DataMember()]
public string Filename
{
get
{
return this.filenameField;
}
set
{
this.filenameField = value;
}
}
}
public static void Test()
{
string Filename = "Image.jpg";
byte[] file = File.ReadAllBytes(Filename);
TestAttachment Attachment = new TestAttachment();
Attachment.Filename = Filename;
Attachment.File = file;
MemoryStream MTOMInMemory = new MemoryStream();
XmlDictionaryWriter TW = XmlDictionaryWriter.CreateMtomWriter(MTOMInMemory, Encoding.UTF8, Int32.MaxValue, "");
DataContractSerializer DCS = new DataContractSerializer(Attachment.GetType());
DCS.WriteObject(TW, Attachment);
TW.Flush();
Console.WriteLine(Encoding.UTF8.GetString(MTOMInMemory.ToArray()));
var v = DeserializeMTOMMessage(Encoding.UTF8.GetString(MTOMInMemory.ToArray()));
File.WriteAllBytes(v.Filename,v.File);
}
public static TestAttachment DeserializeMTOMMessage(string MTOMMessage)
{
try
{
MemoryStream MTOMMessageInMemory = new MemoryStream(UTF8Encoding.UTF8.GetBytes(MTOMMessage));
XmlDictionaryReader TR = XmlDictionaryReader.CreateMtomReader(MTOMMessageInMemory, Encoding.UTF8, XmlDictionaryReaderQuotas.Max);
DataContractSerializer DCS = new DataContractSerializer(typeof(TestAttachment));
return (TestAttachment)DCS.ReadObject(TR);
}
catch
{
return null;
}
}
}
I would be grateful if someone can help me in pointing out where the problem is. I am new to XOP/MTOM and find it hard to track down where the error might be. Either serialization or de-serialization.
Thank you
There's a bug in your code.
Change your method call
MTOMInMemory.Position = 0;
DeserializeMTOMMessage(Encoding.UTF8.GetString(MTOMInMemory.ToArray()));
to
DeserializeMTOMMessage(MTOMInMemory.ToArray())
and the implementation to
public static TestAttachment DeserializeMTOMMessage(byte[] MTOMMessage)
{
try
{
MemoryStream MTOMMessageInMemory = new MemoryStream(MTOMMessage);
XmlDictionaryReader TR = XmlDictionaryReader.CreateMtomReader(MTOMMessageInMemory, Encoding.UTF8, XmlDictionaryReaderQuotas.Max);
DataContractSerializer DCS = new DataContractSerializer(typeof(TestAttachment));
return (TestAttachment)DCS.ReadObject(TR);
}
catch
{
return null;
}
}
what you've done was some double conversion from utf8 to byte array and vice versa, which ended up creating not the original byte array you were using
When writing to a stream the DataContractSerializer uses an encoding different from Unicode-16. If I could force it to write/read Unicode-16 I could store it in a SQL CE's binary column and read it with SELECT CONVERT(nchar(1000), columnName). But the way it is, I can't read it, except programatically.
Can I change the encoding used by System.Runtime.Serialization.DataContractSerializer?
The DataContractSerializer's WriteObject method has overloads which write to a Stream or to a XmlWriter (and XmlDictionaryWriter). The Stream overload will default to UTF-8, so you'll need to use another one. Using a XML Writer instance which writes the XML in UTF-16 do what you needs, so you can either do what #Phil suggested, or you can use the writer returned by XmlDictionaryWriter.CreateTextWriter for which you pass an Encoding.Unicode as a parameter.
public class StackOverflow_10089682
{
[DataContract(Name = "Person", Namespace = "http://my.namespace")]
public class Person
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
}
public static void Test()
{
MemoryStream ms = new MemoryStream();
XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(ms, Encoding.Unicode);
DataContractSerializer dcs = new DataContractSerializer(typeof(Person));
Person instance = new Person { Name = "John Doe", Age = 33 };
dcs.WriteObject(writer, instance);
writer.Flush(); // Don't forget to Flush the writer here
Console.WriteLine("Decoding using UTF-16: {0}", Encoding.Unicode.GetString(ms.ToArray()));
}
}
Have you tried using XmlWriterSettings? Something like
var s = new DataContractSerializer (typeof(Thing));
using(var wr = XmlTextWriter.Create(
#"test.xml", new XmlWriterSettings{Encoding=Encoding.UTF32}))
{
s.WriteObject(wr, new Thing{Foo="bar"});
}
public class Thing
{
public string Foo { get; set; }
}
Specify the Encoding you require.
Hey all, I am developing a site for work that will push info from a database into Wordpress using Wordpress XML RPC. I can grab info and post it just fine, however when I get to the point of uploading images it seems to work(no runtime errors/image in WP Media Tab) however it uploads a broken image link. It appears it is somehow no getting the data from my image and I am not certain why here is some of my code.
MemoryStream ms = new MemoryStream();
System.Drawing.Image img = System.Drawing.Image.FromFile(HttpContext.Current.Server.MapPath("_Images/DownloadButton-PSD.png"));
img.Save(ms, ImageFormat.Png);
byte[] imagebytes = new byte[ms.Length];
ms.Position = 0;
ms.Read(imagebytes, 0, Convert.ToInt32(ms.Length));
after that code loads the image info I pass it to the function in the format of a Data variable
var data = new Data
{
Base64 = Convert.ToBase64String(imagebytes),
Name = "DownloadButton-PSD.png",
Type = "image/png",
Overwrite = false,
};
_wpWrapper.UploadFile(data);
FYI: I am also using the dll's from
http://joeblogs.codeplex.com/
for my project
The Data Class looks like this:
public class Data
{
public string Name { get; set; }
public string Type { get; set; }
public string Base64 { get; set; }
public bool Overwrite { get; set; }
}
The Upload File Function looks like this:
public void UploadFile(Data data)
{
var xmlRpcData = Map.From.Data(data);
var result = _wrapper.UploadFile(this.BlogID, Username, Password, xmlRpcData);
}
In JoeBlogs library try using the class MetaWeblogWrapper and method: MediaObjectInfo NewMediaObject(MediaObject mediaObject) - for upload image.