3
votes

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

2
I'm confused as to what you are trying to accomplish here. Are you trying to read some JSON from the cloud and maybe deserialize it into an object? Or are you trying to serialize some object and store the JSON into the cloud? Or something else? What you are actually doing is trying to serialize the stream object itself, which won't work. The exception is happening because Json.Net is trying to treat the stream like a POCO and read all the public properties from it. One of those properties is Length, which is not readable on a stream that is opened for writing.Brian Rogers
@BrianRogers The code is only to demonstrate the problem. What I want to achive is a json service that returns a stream for read data from or write data to Azure Blob Storage.Attilio Gelosa

2 Answers

6
votes

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>();
1
votes

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;