Live SDK - Uploading XML file trough Memory Stream - c#

I have a bit of a problem with the client.UploadAsync method of Live SDK (SkyDrive SDK). My code for some reason doesn't work or more specifically it uploads an empty file. It doesn't throw any error and the serialization to stream works (I know that for sure).
It even seems that the Memory Stream is OK. (since I have no tool to really see the data in it I just guess it is OK by looking at its 'Length' property).
The UploadAsync method is fine as well or at least it worked well when I first serialized the data into a .xml file in IsolatedStorage then read it with IsolatedStorageFileStream and then eventualy send that stream. (then it uploaded the data)
Any advice on why this may be happening?
public void UploadFile<T>(string skyDriveFolderID, T data, string fileNameInSkyDrive)
{
this.fileNameInSkyDrive = fileNameInSkyDrive;
{
try
{
memoryStream = new MemoryStream();
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Indent = true;
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (XmlWriter xmlWriter = XmlWriter.Create(memoryStream, xmlWriterSettings))
{
serializer.Serialize(xmlWriter, data);
}
client.UploadAsync(skyDriveFolderID, fileNameInSkyDrive, true, memoryStream, null);
}
catch (Exception ex)
{
if (memoryStream != null) { memoryStream.Dispose(); }
}
}
}

You have to "rewind" the memorystream to the start before calling the UploadAsync method. Imagine the memorystream being like a tape which you record things on. The "read/write-head" is always floating over some point of the tape, which is the end in your case because you just wrote all serialized data onto it. The uploading method tries to read from it by moving forward on the tape, realizing it is already at its end. Thus you get an empty file uploaded.
The method you need for rewinding is:
memoryStream.Seek(0, SeekOrigin.Begin);
Also, it is good practice to use the using directive for IDisposable objects, which the memorystream is. This way you don't need a try {...} finally { ...Dispose(); } (this is done by the using).
Your method could then look like:
public void UploadFile<T>(string skyDriveFolderID, T data, string fileNameInSkyDrive)
{
this.fileNameInSkyDrive = fileNameInSkyDrive;
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Indent = true;
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (var memoryStream = new MemoryStream())
{
using (var xmlWriter = XmlWriter.Create(memoryStream, xmlWriterSettings))
{
serializer.Serialize(xmlWriter, data);
}
memoryStream.Seek(0, SeekOrigin.Begin);
client.UploadAsync(skyDriveFolderID, fileNameInSkyDrive, true, memoryStream, null);
}
}

Related

Could XmlSerializer and MemoryStream return an array of 0 value bytes?

I have a method that saves an instance of my custom class to a file. One time I noticed that my application fails to start, because this file is filled with 0-value bytes (null characters). This has never happened before, it seemed to work just fine. Does anyone see something odd with this code? Something that can cause the serializer or the memory stream to return an array of zero values? Or should I suspect it's the work of another application?
private readonly XmlSerializer _serializer = new XmlSerializer(typeof(MySettings));
public void Save(MySettings config)
{
using (var stream = new MemoryStream())
{
_serializer.Serialize(stream, config);
byte[] binaryConfig = stream.ToArray();
File.WriteAllBytes(_configFilePath, binaryConfig);
}
}
Wouldn't it be simpler to use something like this?
XmlSerializer x = new XmlSerializer(typeof(MySettings));
using (FileStream stream = new FileStream(_configFilePath, FileMode.Create, FileAccess.Write))
{
x.Serialize(stream, config);
stream.Close();
}
The XML file should not contain any 0-bytes or nul characters, as your object is translated to XML text during serialization. You can simply open the XML file using a text editor to have a look at the file contents.

Uploading an in-memory file to Skydrive in a windows store app?

I've got the following Windows Phone 8 code to upload a file to Skydrive. The file is a collection of SQLite objects serialized to XML
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Indent = true;
XmlSerializer xs = new XmlSerializer(typeof(ObservableCollection<MyObject>));
using (MemoryStream ms = new MemoryStream())
{
using (var xmlWriter = XmlWriter.Create(ms, xmlWriterSettings))
{
xs.Serialize(xmlWriter, MyObject);
}
ms.Seek(0, SeekOrigin.Begin);
result = await App.LiveConnectClient.UploadAsync("me/skydrive", "sync.xml", ms, OverwriteOption.Overwrite);
}
I'm trying to port this code to a c# Windows Store app, but I've come up against a problem. Under the windows store version of the Live SDK, UploadAsync is not available. BackgroundUploadAsync is the closes thing, and it only accepts Windows.Storage.Streams.IInputStream. I can't directly create IInputStream like I can with 'MemoryStream' in the code above.
All the examples I've found online detail how to upload a file from isolatedStorage. I don't want to have to write the file to isolatedStorage just to be able to upload it.
Can someone share some code for uploading a stream? Failing that, does anyone have a method to convert MemoryStream to Windows.Storage.Streams.IInputStream
Check out InMemoryRandomAccessStream, which is the replacement in WinRT for MemoryStream (though MemoryStream still exists).
They provide some neat extensions for it in System.Runtime.InteropServices.WindowsRuntime.
using System.Runtime.InteropServices.WindowsRuntime;
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Indent = true;
XmlSerializer xs = new XmlSerializer(typeof(ObservableCollection<MyObject>));
using (InMemoryRandomAccessStream ms = new InMemoryRandomAccessStream())
{
using (var xmlWriter = XmlWriter.Create(ms.AsStream(), xmlWriterSettings))
{
xs.Serialize(xmlWriter, MyObject);
}
ms.Seek(0);
result = await App.LiveConnectClient.UploadAsync("me/skydrive", "sync.xml", ms.GetInputStreamAt(0), OverwriteOption.Overwrite);
}
Should work. Hope this helps and Happy Coding!

Serializing a simple array with XmlSerializer

Its late and fully possible I'm missing something obvious but what is it?
I'm trying to create a backing property which reveals an int array as serialized (which is then used to build up a Queue).
I'm pretty sure this is right but the getter always return a blank string, even when there are values in there (not that it should ever return a blank string.
Here is my code:
readonly Lazy<XmlSerializer> _queueSerializer = new Lazy<XmlSerializer>(() => new XmlSerializer(typeof(int[])));
[StringLength(1000)]
public string _MostRecentPlayers
{
get
{
var stream = new MemoryStream();
_queueSerializer.Value.Serialize(stream, _mostRecentPlayers.ToArray());
return new StreamReader(stream).ReadToEnd();
}
set
{
if (value.IsEmpty())
{
_mostRecentPlayers.Clear();
return;
}
MemoryStream stream = new MemoryStream(Encoding.ASCII.GetBytes(value));
var tempQueue = _queueSerializer.Value.Deserialize(stream) as int[];
_mostRecentPlayers.Clear();
tempQueue.ForEach(_mostRecentPlayers.Enqueue);
}
}
readonly Queue<int> _mostRecentPlayers = new Queue<int>(_mostRecentAmountTracked);
You haven't rewound the stream; it is positioned at the end. Set .Position = 0 before reading it. Or easier, just serialize to a StringWriter, or if you really want to use a MemoryStream, pass the (oversized) backing array from GetBuffer() along with the .Length to an Encoding and call GetString().
using(var sw = new StringWriter()) {
_queueSerializer.Value.Serialize(sw, _mostRecentPlayers.ToArray());
xml = sw.ToString();
}
or for ASCII (see comments):
using(var ms = new MemoryStream()) {
var settings = new XmlWriterSettings {
Encoding = Encoding.ASCII
};
using(var xw = XmlWriter.Create(ms, settings)) {
_queueSerializer.Value.Serialize(xw, _mostRecentPlayers.ToArray());
}
xml = Encoding.ASCII.GetString(ms.GetBuffer(), 0, (int)ms.Length);
}
Also, unless it is unlikely that you will serialize in the exe, I would suggest simplifying to just:
static readonly XmlSerializer _queueSerializer =new XmlSerializer(typeof(int[]));
Finally, note that xml is quite verbose as a mechansim to throw some ints around. CSV would seem a lot simpler (assuming you want text).

Difficulty with IsolatedStorageFile

I'm building a Windows Phone 7 app in Silverlight. I'm having difficulty using IsolatedStorageFile.
The following method is supposed to write some data to a file:
private static void writeToFile(IList<Story> stories)
{
IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication();
using (IsolatedStorageFileStream stream = storage.OpenFile(STORIES_FILE, FileMode.Append))
{
using (StreamWriter writer = new StreamWriter(stream))
{
StringBuilder toJson = new StringBuilder();
IList<StoryJson> storyJsons = (from story in stories
where !storageStories.Contains(story)
select story.ToStoryJson()).ToList();
writer.Write(JsonConvert.SerializeObject(storyJsons));
}
}
#if DEBUG
StreamReader reader = new StreamReader(storage.OpenFile(STORIES_FILE, FileMode.Open));
string contents = reader.ReadToEnd();
#endif
}
The DEBUG at the end is for me to check that the data is actually being written. I have verified that it is. This method is called 6+ times. Each time, more data is appended.
However, when I go to read the data, the only JSON I get back is that which I wrote in one call of writeToFile(). Here is my method to read:
private static IList<Story> storageStories;
private static IList<Story> readFromStorage()
{
if (storageStories != null)
{
return storageStories;
}
IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication();
if (! storage.FileExists(STORIES_FILE))
{
storage.CreateFile(STORIES_FILE);
storageStories = new List<Story>();
return storageStories;
}
string contents;
using (IsolatedStorageFileStream stream = storage.OpenFile(STORIES_FILE, FileMode.OpenOrCreate))
{
using (StreamReader reader = new StreamReader(stream))
{
contents = reader.ReadToEnd();
}
}
JsonSerializer serializer = new JsonSerializer();
storageStories = JArray.Parse(contents).Select(storyData => storyOfJson(serializer, storyData)).ToList();
return storageStories;
}
What could I be doing wrong here? Am I writing to the file incorrectly? I'm pretty sure that the only data that is able to be read back is from the first write.
Update: I added two Flush() calls, but it crashes:
private static void writeToFile(IList<Story> stories)
{
IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication();
using (IsolatedStorageFileStream stream = storage.OpenFile(STORIES_FILE, FileMode.Append))
{
using (StreamWriter writer = new StreamWriter(stream))
{
StringBuilder toJson = new StringBuilder();
IList<StoryJson> storyJsons = (from story in stories
where !storageStories.Contains(story)
select story.ToStoryJson()).ToList();
writer.Write(JsonConvert.SerializeObject(storyJsons));
writer.Flush();
}
// FAILS
// "Cannot access a closed file." {System.ObjectDisposedException}
stream.Flush();
}
}
If I comment out the stream.Flush() but leave writer.Flush(), I have the same problem.
Update 2: I added some print statements. Looks like everything is getting serialized:
Serializing for VID 43
Serializing for VID 17
Serializing for VID 6
Serializing for VID 33
Serializing for VID 4
Serializing for VID 5
Serializing for VID 3
But only the first set is actually being read back:
Deserializing stories with vid: 43
I have run the test a few more times. I'm pretty sure that only the first item is ever being read back.
At first glance it sounds like your stream data is not being flushed to disk.
You are probably thinking that the using block will perform a flush when it Disposes the stream. However I have found that is not always the case and sometimes it is best to force a Flush() at the end.
I remember recently in a codebase that we received from a Microsoft team to port to WP7 that they were forcing a Flush. I questioned it initially, thinking that the Dispose should handle that, however as it was working and we were on a short deadline I did not investigate it further.
Give it go, see what happens... :)
Have you tried explicitly calling
writer.Close()
rather than relying on writer.Dispose()

How to write System.Xml.Linq.XElement using XmlWriter to a stream

I have an XElement instance and I wish to write to a stream using XmlWriter class. Why? Well, one of the configuration settings defines whether to use binary Xml or not. Based on this setting a suitable XmlWriter instance is created - either by XmlWriter.Create(stream) or XmlDictionaryWriter.CreateBinaryWriter(stream)).
Anyway, I am trying the following code, but it leaves the stream empty:
using (var stream = new MemoryStream())
{
var xmlReader = new XDocument(xml).CreateReader();
xmlReader.MoveToContent();
var xmlWriter = GetXmlWriter(stream);
xmlWriter.WriteNode(xmlReader, true);
return stream.ToArray();
}
I have checked, xmlReader is properly aligned after MoveToContent at the root XML element.
I must be doing something wrong, but what?
Thanks.
You haven't shown what GetXmlWriter does... but have you tried just flushing the writer?
xmlWriter.Flush();
Alternatively, wrap the XmlWriter in another using statement:
using (var stream = new MemoryStream())
{
var xmlReader = new XDocument(xml).CreateReader();
xmlReader.MoveToContent();
using (var xmlWriter = GetXmlWriter(stream))
{
xmlWriter.WriteNode(xmlReader, true);
}
return stream.ToArray();
}
You might want to do the same for the XmlReader as well, although in this particular case I don't believe you particularly need to.
Having said all this, I'm not entirely sure why you're using an XmlReader at all. Any reason you can't just find the relevant XElement and use XElement.WriteTo(XmlWriter)? Or if you're trying to copy the whole document, just use XDocument.WriteTo(XmlWriter)

Categories

Resources