Reputation: 561
I have a little problem. My environment is a console app in .NET Core 2.1.
Look at the this code:
private static void Main(string[] args)
{
try
{
Console.WriteLine($"Test starts: {DateTime.Now.ToString("o")}");
string connectionString = "[My connection string]";
string containerName = "mycontainer";
CloudStorageAccount account = CloudStorageAccount.Parse(connectionString);
CloudBlobClient serviceClient = account.CreateCloudBlobClient();
CloudBlobContainer container = serviceClient.GetContainerReference(containerName);
container.CreateIfNotExistsAsync().Wait();
CloudBlockBlob cloudBlockBlob = container.GetBlockBlobReference($"{containerName}/Test.txt");
CloudBlobStream cloudBlobStream = cloudBlockBlob.OpenWriteAsync().Result;
string json = JsonConvert.SerializeObject(cloudBlobStream);
Console.WriteLine($"Test ends: {DateTime.Now.ToString("o")}");
}
catch (Exception e)
{
string stackTrace = e.StackTrace;
while(e != null)
{
Console.WriteLine(e.Message);
e = e.InnerException;
}
Console.WriteLine(stackTrace);
}
Console.Write("Press any key to exit...");
Console.ReadKey();
}
When I try to serialize the CloudBlobStream
object with the command string json = JsonConvert.SerializeObject(cloudBlobStream);
, I obtain the following Exception:
Error getting value from 'Length' on 'Microsoft.WindowsAzure.Storage.Blob.BlobWriteStream'. Specified method is not supported. at Newtonsoft.Json.Serialization.ExpressionValueProvider.GetValue(Object target) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType) at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType) at Newtonsoft.Json.JsonConvert.SerializeObjectInternal(Object value, Type type, JsonSerializer jsonSerializer) at AzureBlobStreamSerializationTest.Program.Main(String[] args) in C:\Projects\AzureBlobStreamSerializationTest\AzureBlobStreamSerializationTest\Program.cs:line 28
Any idea on how to solve the problem?
Regards, Attilio
Upvotes: 4
Views: 4277
Reputation: 129807
In your code it looks like you are literally trying to serialize a Stream
object as if it were data. This will not work. The exception is happening because the serializer is trying to read all the public properties from the Stream
object. One of these properties is Length
, which is not readable when the stream is opened for writing.
I think you are misunderstanding how to use streams with Json.Net and Azure storage blobs. The JsonConvert
class is really just a façade, and unfortunately its SerializeObject()
method does not have an overload that supports streams. To work with streams using Json.Net, you need to use a JsonSerializer
instance along with a StreamWriter
and JsonTextWriter
if you're serializing to the stream, or StreamReader
and JsonTextReader
if you're deserializing from it.
Here are a couple of extension methods which you might find helpful and instructive:
public static class BlobExtensions
{
public static async Task SerializeObjectToBlobAsync(this CloudBlockBlob blob, object obj)
{
using (Stream stream = await blob.OpenWriteAsync())
using (StreamWriter sw = new StreamWriter(stream))
using (JsonTextWriter jtw = new JsonTextWriter(sw))
{
JsonSerializer ser = new JsonSerializer();
ser.Serialize(jtw, obj);
}
}
public static async Task<T> DeserializeObjectFromBlobAsync<T>(this CloudBlockBlob blob)
{
using (Stream stream = await blob.OpenReadAsync())
using (StreamReader sr = new StreamReader(stream))
using (JsonTextReader jtr = new JsonTextReader(sr))
{
JsonSerializer ser = new JsonSerializer();
return ser.Deserialize<T>(jtr);
}
}
}
Now let's say you have a class Item
representing some data that you want to serialize to JSON and store into an Azure blob:
class Item
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
}
Here's how you can do that using the first extension method:
...
var item = new Item { Id = 1, Name = "Widget", Price = 2.5 };
cloudBlockBlob.SerializeObjectToBlobAsync(item).Wait();
Conversely, if you want to retrieve the JSON from the blob and deserialize it back into an Item
, you can use the other extension method:
var item = cloudBlockBlob.DeserializeObjectFromBlobAsync<Item>().Result;
Note: if you are using these methods from within an async
method, you should use the await
syntax instead of using Wait()
and .Result
, respectively:
await cloudBlockBlob.SerializeObjectToBlobAsync(item);
...
var item = await cloudBlockBlob.DeserializeObjectFromBlobAsync<Item>();
Upvotes: 8
Reputation: 24569
The paramter of the CloudBlobContainer.GetBlockBlobReference should be blob name.
please remove the containerName from your code.
CloudBlockBlob cloudBlockBlob = container.GetBlockBlobReference("Test.txt"); //remove the container name from original code
According to the error execption, it indicates that JsonConvert.SerializeObject doesn't support CloudBlobStream as paramter.
Any idea on how to solve the problem?
If you want to get text string, you could use the cloudBlockBlob.DownloadTextAsync()
.
So please change your code to following
container.CreateIfNotExistsAsync().Wait();
CloudBlockBlob cloudBlockBlob = container.GetBlockBlobReference("Test.txt"); //make sure that blob is existing
var json = cloudBlockBlob.DownloadTextAsync().Result;
Upvotes: 1