Kevin Buchs
Kevin Buchs

Reputation: 2872

Failure in Microsoft.Azure.Cosmos.Container.ReadItemAsync() - json parsing - unexpected character

I am porting some C# code from Microsoft.Azure.Documents to Microsoft.Azure.Cosmos. I created a small test case to try to get it working just accessing CosmosDB before testing it with my full application. I'll add my full code further below, but here is what I believe is the relevant content.

try {
   Container container = client.GetContainer(DocumentDBDatabaseName, collectionId);
   RequestOptions requestOptions = new RequestOptions();
   ThroughputProperties tputprops = await container.ReadThroughputAsync(requestOptions);
   Console.WriteLine($"Throughput: {tputprops.Throughput}");
   ContainerProperties containerProperties = await container.ReadContainerAsync();
   Console.WriteLine($"Container props: {containerProperties}");
   // We don't use any partition keys, so use PartitionKey.None
   ItemRequestOptions iro = new ItemRequestOptions();
   ItemResponse<string> result = await container.ReadItemAsync<string>(documentDBStorageId, PartitionKey.None, iro);
   return result;  // successful return
} catch (CosmosException exc) {
   Console.WriteLine("{0} error occurred: {1}", exc.StatusCode, exc);
} catch (Exception exc) {
   Console.WriteLine("Error: {0}", exc);
}

Output looks like this:

Throughput: 400
Container props: Microsoft.Azure.Cosmos.ContainerProperties
Error: Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: {. Path '', line 1, position 1.
   at Newtonsoft.Json.JsonTextReader.ReadStringValue(ReadType readType)
   at Newtonsoft.Json.JsonTextReader.ReadAsString()
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadForType(JsonReader reader, JsonContract contract, Boolean hasConverter)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonSerializer.Deserialize[T](JsonReader reader)
   at Microsoft.Azure.Cosmos.CosmosJsonDotNetSerializer.FromStream[T](Stream stream)
   at Microsoft.Azure.Cosmos.CosmosJsonSerializerWrapper.FromStream[T](Stream stream)
   at Microsoft.Azure.Cosmos.CosmosSerializerCore.FromStream[T](Stream stream)
   at Microsoft.Azure.Cosmos.CosmosResponseFactoryCore.ToObjectpublic[T](ResponseMessage responseMessage)
   at Microsoft.Azure.Cosmos.CosmosResponseFactoryCore.b__8_0[T](ResponseMessage cosmosResponseMessage)
   at Microsoft.Azure.Cosmos.CosmosResponseFactoryCore.ProcessMessage[T](ResponseMessage responseMessage, Func`2 createResponse)
   at Microsoft.Azure.Cosmos.CosmosResponseFactoryCore.CreateItemResponse[T](ResponseMessage responseMessage)
   at Microsoft.Azure.Cosmos.ContainerCore.d__56`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.Cosmos.ClientContextCore.d__38`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at TestCosmos_ns.TestCosmos_cl.d__3.MoveNext() in C:\Users\buchs\Work-NCS\TestCosmos2\TestCosmos\Program.cs:line 46

The documentDBstorageid is simply a string which matches the document ID found in the database. The database does not use partition keys. Obviously the container is fine as I can gather other data from it without exception.

Here is the fuller code

// Package: Microsoft.Azure.Cosmos --version 3.12.0
using System;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos;

namespace TestCosmos_ns
{
    class TestCosmos_cl
    {
        private static string DocumentDBDatabaseName = "mydbname";
        public static void Main(string[] args)
        {
            Task Ta = AsyncMain(args);
            Ta.Wait();  // Wait for all async tasks to close.
        }

        public static async Task AsyncMain(string[] args)
        {
            // corresponds to CosmosDB container
            string EmployeeCollection = "directory";
            string docId2 = GetDocumentDBStorageId("company", "companyid", "locationx");
            string u = await GetDocumentById(EmployeeCollection, docId2);
            Console.WriteLine("GetDocumentById returns:");
            Console.WriteLine(u);
            bool v = DocumentExists(EmployeeCollection, docId2);
            Console.WriteLine("DocumentExists() returns:");
            Console.WriteLine(v);
        }
        public static async Task<string> GetDocumentById(string collectionId, string documentDBStorageId)
        {
            CosmosClient client = GetDocumentDBClient();
            try {
                Container container = client.GetContainer(DocumentDBDatabaseName, collectionId);
                RequestOptions requestOptions = new RequestOptions();
                ThroughputProperties tputprops = await container.ReadThroughputAsync(requestOptions);
                Console.WriteLine($"Throughput: {tputprops.Throughput}");
                ContainerProperties containerProperties = await container.ReadContainerAsync();
                Console.WriteLine($"Container props: {containerProperties}");
                // We don't use any partition keys, so use PartitionKey.None
                ItemRequestOptions iro = new ItemRequestOptions();
                ItemResponse<string> result = await container.ReadItemAsync<string>(documentDBStorageId, PartitionKey.None, iro);
                return result;  // successful return
            } catch (CosmosException exc) {
                Console.WriteLine("{0} error occurred: {1}", exc.StatusCode, exc);
            } catch (Exception exc) {
                Console.WriteLine("Error: {0}", exc);
            }
            return "";  // return in exception situations
        }

        public static bool DocumentExists(string collectionId, string documentDBStorageId)
        {
            // OK, totally cheating. Just try to retrieve the document, if we get back blank, then there
            // was an exception and it doesn't exist.
            Task<string> getResult = GetDocumentById(collectionId, documentDBStorageId);
            getResult.Wait();
            string result = getResult.Result;
            if (result.Length == 0) {
                return false;
            }
            return true;
        }
        private static CosmosClient TheCosmosClient = null;
        public static CosmosClient GetDocumentDBClient()
        {
            string DocumentDBEndpointUrl = "https://mydb.documents.azure.com:443/";
            string DocumentDBPrimaryKey = "myprimarykey...==";
            TheCosmosClient = TheCosmosClient ?? new CosmosClient(DocumentDBEndpointUrl, DocumentDBPrimaryKey);
            return TheCosmosClient;
        }

        private static string NormalizePath(string result)
        {
            string toReplace = " ,.<>?;:'\"`~!@#$%^&*()-=+";

            foreach (char c in toReplace)
                result = result.Replace(c, '_');

            result = result.Replace("__", "_");
            return result;
        }
        public static string GetDocumentDBStorageId(string companyName, string companyId, string locationName) => NormalizePath($"Employees_{companyName}_{companyId}_{locationName}");
    }
}

Upvotes: 0

Views: 2820

Answers (2)

Steve Johnson
Steve Johnson

Reputation: 8690

Please try this:

use this code:

ItemResponse<JObject> result = await container.ReadItemAsync<JObject>(documentDBStorageId, PartitionKey.None, iro);

instead of:

ItemResponse<string> result = await container.ReadItemAsync<string>(documentDBStorageId, PartitionKey.None, iro);

Upvotes: 2

Mark Brown
Mark Brown

Reputation: 8783

It appears you are passing multiple values for id ("company", "companyid", "locationx")

You can only pass a single value for id for ReadItemAsync(). If you want to pass multiple values you can use a query with an IN() clause.

Upvotes: 0

Related Questions