How to download and open XML file via SFTP? - c#

I need to open XML file (create XmlDocument) without creating local copy. Using SSH.NET, I came up with this code:
var connectionInfo = new ConnectionInfo("host",
"username",
new PasswordAuthenticationMethod("username", "password"));
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
System.IO.MemoryStream mem = new System.IO.MemoryStream();
client.DownloadFile("filename.xml", mem);
mem.Position=0;
using(XmlReader reader = XmlReader.Create(mem))
{
var docc = new XmlDocument();
docc.Load(mem);
}
client.Disconnect();
}
But is gets stuck on docc.Load(mem). What could be the problem?
mem object looks like this:

Note that here:
using(XmlReader reader = XmlReader.Create(mem))
{
var docc = new XmlDocument();
docc.Load(mem);
}
You are not using variable reader at all. Either change to
using(XmlReader reader = XmlReader.Create(mem))
{
var docc = new XmlDocument();
docc.Load(reader);
}
or remove reader at all:
docc.Load(mem);

While the answer by #Evk solves your immediate problem, your code is still inefficient.
Use SftpClient.OpenRead to directly stream the file to XmlReader:
using (XmlReader reader = XmlReader.Create(client.OpenRead("filename.xml"))
{
// process the XML
}
or to XmlDocument:
var docc = new XmlDocument();
docc.Load(client.OpenRead("filename.xml"));
This way, you do not waste a memory by creating another copy of the file in MemoryStream.

It looks like you wrote xml file to stream and now, stream is pointed at the end of xml file. Try setting stream.position to 0 before loading.
stream.Position = 0;

Try this:
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
System.IO.MemoryStream mem = new System.IO.MemoryStream();
client.DownloadFile("filename.xml", mem);
// Set stream position
mem.Position = 0;
using(XmlReader reader = XmlReader.Create(mem))
{
var docc = new XmlDocument();
docc.Load(mem);
}
client.Disconnect();
}
I would also change the code to dispose the memory stream correctly...

Related

C# Spire Document.SaveToStream not working

I have the following code but it is just creating a 0kb empty file.
using (var stream1 = new MemoryStream())
{
MemoryStream txtStream = new MemoryStream();
Document document = new Document();
fileInformation.Stream.CopyTo(stream1);
document.LoadFromStream(stream1, FileFormat.Auto);
document.SaveToStream(txtStream, FileFormat.Txt);
StreamReader reader = new StreamReader(txtStream);
string text = reader.ReadToEnd();
System.IO.File.WriteAllText(fileName + ".txt", text);
}
I know the data is successfully loaded into document because if do document.SaveToTxt("test.txt", Encoding.UTF8);
instead of the SaveToStream line it exports the file properly.
What am I doing wrong?
When copying a stream, you need to take care to reset the position to 0 if copying. As seen in the answer here, you can do something like this to your streams:
stream1.Position = 0;
txtStream.Position = 0;

Download blob storage and return Json object

I am trying to download a .json blob that I have stored in a container in the Azure Storage using Newtonsoft.Json to write it to an object.
I am doing this by calling:
(CloudBlockBlob) blob.DownloadToStream(stream);
However, instead of writing the stream to a file in the local app directory, I want to return the json object doing Json(result)
This is what I have tried:
using (var stream = new MemoryStream())
{
blob.DownloadToStream(stream);
var serializer = new JsonSerializer();
using (var sr = new StreamReader(stream))
{
using (var jsonTextReader = new JsonTextReader(sr))
{
result = serializer.Deserialize(jsonTextReader);
}
}
}
At the end my jsonTextReader variable is empty and the object null
What can I do to accomplish this?
Thank you
Both the question and accepted answer start by copying the entire stream into a MemoryStream which is effectively a big byte array in memory. This step is unnecessary - it's more memory-efficient to stream the blob data directly to the object without buffering the bytes first:
using (var stream = await blob.OpenReadAsync())
using (var sr = new StreamReader(stream))
using (var jr = new JsonTextReader(sr))
{
result = JsonSerializer.CreateDefault().Deserialize<T>(jr);
}
Please reset the stream's position to 0 after reading the blob into the stream. So your code would be:
using (var stream = new MemoryStream())
{
blob.DownloadToStream(stream);
stream.Position = 0;//resetting stream's position to 0
var serializer = new JsonSerializer();
using (var sr = new StreamReader(stream))
{
using (var jsonTextReader = new JsonTextReader(sr))
{
var result = serializer.Deserialize(jsonTextReader);
}
}
}
In case you don't care for streaming and want a short and concise way:
var json = await blockBlob.DownloadTextAsync();
var myObject = JsonConvert.DeserializeObject<MyObject>(json);

Converting Class to XML to string

I'm using XMLSerializer to serialize a class into a XML. There are plenty of examples to this and save the XML into a file. However what I want is to put the XML into a string rather than save it to a file.
I'm experimenting with the code below, but it's not working:
public static void Main(string[] args)
{
XmlSerializer ser = new XmlSerializer(typeof(TestClass));
MemoryStream m = new MemoryStream();
ser.Serialize(m, new TestClass());
string xml = new StreamReader(m).ReadToEnd();
Console.WriteLine(xml);
Console.ReadLine();
}
public class TestClass
{
public int Legs = 4;
public int NoOfKills = 100;
}
Any ideas on how to fix this ?
Thanks.
You have to position your memory stream back to the beginning prior to reading like this:
XmlSerializer ser = new XmlSerializer(typeof(TestClass));
MemoryStream m = new MemoryStream();
ser.Serialize(m, new TestClass());
// reset to 0 so we start reading from the beginning of the stream
m.Position = 0;
string xml = new StreamReader(m).ReadToEnd();
On top of that, it's always important to close resources by either calling dispose or close. Your full code should be something like this:
XmlSerializer ser = new XmlSerializer(typeof(TestClass));
string xml;
using (MemoryStream m = new MemoryStream())
{
ser.Serialize(m, new TestClass());
// reset to 0
m.Position = 0;
xml = new StreamReader(m).ReadToEnd();
}
Console.WriteLine(xml);
Console.ReadLine();
There's the [Serializabe] attribute missing on class TestClass and you have to set the position of the memory stream to the beginning:
XmlSerializer ser = new XmlSerializer(typeof(TestClass));
MemoryStream m = new MemoryStream();
ser.Serialize(m, new TestClass());
m.Position = 0;
string xml = new StreamReader(m).ReadToEnd();
Console.WriteLine(xml);
Console.ReadLine();
Your memory stream is not closed, and is positioned at the end (next available location to write in). My guess is that you must Close it, or Seek to its beginning. The way you do you don't read anything because you are already at the end of stream. So add Seek() after you serialize objects. Like this:
XmlSerializer ser = new XmlSerializer(typeof(TestClass));
MemoryStream m = new MemoryStream();
ser.Serialize(m, new TestClass());
m.Seek(0, SeekOrigin.Begin); //<-- ADD THIS!
string xml = new StreamReader(m).ReadToEnd();
Console.WriteLine(xml);
Console.ReadLine();

Deserialize a string

Currently I'm using this code to deserialize a file
StreamReader str = new StreamReader(reply);
System.Xml.Serialization.XmlSerializer xSerializer = new System.Xml.Serialization.XmlSerializer(typeof(imginfo));
imginfo res = (imginfo)xSerializer.Deserialize(str);
How should I modify the code if reply is a string and not a path to an xml file?
Basically, you use an XmlReader chained to a StringReader:
imginfo res;
using(var sr = new StringReader(xml)) // "xml" is our string containing xml
using(var xr = XmlReader.Create(sr)) {
res = (imginfo)xSerializer.Deserialize(xr);
}
//... use "res"
or as Anders notes:
imginfo res;
using(var sr = new StringReader(xml)) // "xml" is our string containing xml
res = (imginfo)xSerializer.Deserialize(sr);
}
//... use "res"
Use StringReader instead of StreamReader. No other change needed.

"Root element not found" - when reading memory stream

I have a class that i store in a list.
I serialize it ...
XmlDocument xd = new XmlDocument();
MemoryStream ms = new MemoryStream();
XmlSerializer xm = new XmlSerializer(typeof(List<BugWrapper>));
xm.Serialize(ms, _bugs);
StreamReader sr = new StreamReader(ms);
string str = sr.ReadToEnd();
xd.Load(ms);
I looked into str and found it to be empty, the collection however has an object.
Any ideas into why this happens?
Yes - you're saving to the memory stream, leaving it at the end. You need to "rewind" it with:
ms.Position = 0;
just before you create the StreamReader:
xm.Serialize(ms, _bugs);
ms.Position = 0;
StreamReader sr = new StreamReader(ms);
string str = sr.ReadToEnd();
However, you then need to rewind it again before you load into the XmlDocument unless you remove those last two lines, which I suspect were just for debugging. Just for good measure, let's close the memory stream as well when we're done with it:
using (MemoryStream stream = new MemoryStream())
{
XmlSerializer serializer = new XmlSerializer(typeof(List<BugWrapper>));
seralizer.Serialize(stream, _bugs);
stream.Position = 0;
XmlDocument doc = new XmlDocument();
doc.Load(stream);
}

Categories

Resources