I have json data sitting in Cosmos DB which I want delivered to an angular app. The angular app can handle various json structures and allow the user to modify the data and push back to Cosmos (via an API - see below).
I am trying to build a simple C# API that will just be used to query data from Cosmos and push the "raw" json back to angular without the need to create classes for each type of json object I am storing. The API is just the mechanism for handing data to Angular from Cosmos and pushing data back into Cosmos from Angular.
I have not found an easy way to do this as all approaches seem to require a structured class object to retrieve the data from Cosmos and save it back.
Any suggestions how this can be achieved?
-- EDIT --
This is some sample code I was using with dynamic and this works in terms of retrieving the data but it wont serialize into Json in the middleware (see exception below).
public async Task<dynamic> GetItemAsync(string id)
{
try
{
var response = await this._container.ReadItemAsync<dynamic>(id, new PartitionKey(id));
return response.Resource;
}
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return null;
}
}
[15:12:25 ERR] An unhandled exception has occurred while executing the request. <s:Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware> System.NotSupportedException: The collection type 'Newtonsoft.Json.Linq.JObject' is not supported. at System.Text.Json.JsonPropertyInfoNotNullable`4.GetDictionaryKeyAndValueFromGenericDictionary(WriteStackFrame& writeStackFrame, String& key, Object& value) at System.Text.Json.JsonPropertyInfo.GetDictionaryKeyAndValue(WriteStackFrame& writeStackFrame, String& key, Object& value) at System.Text.Json.JsonSerializer.HandleDictionary(JsonClassInfo elementClassInfo, JsonSerializerOptions options, Utf8JsonWriter writer, WriteStack& state) at System.Text.Json.JsonSerializer.Write(Utf8JsonWriter writer, Int32 originalWriterDepth, Int32 flushThreshold, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json, Object value, Type inputType, JsonSerializerOptions options, CancellationToken cancellationToken) at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding) at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|21_0(ResourceInvoker invoker, IActionResult result) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
I also tried using System.Text.JSON with JSONDocument but in retrieving the data it complains that it requires a constructor as part of the class?
CodePudding user response:
The reason this is happening is because Microsoft.Azure.Cosmos
still uses Newtonsoft.Json
to serialize and deserialize.
Your ASP.NET Core application is using System.Text.Json
. When working with Cosmos in an ASP.NET Core application, I will switch the solution to use Newtonsoft.Json
instead of System.Text.Json
. You can do this by adding this package:
Microsoft.AspNetCore.Mvc.NewtonsoftJson
- If you are using .NET Core 3.1, use version
3.1.*
- If you are using .NET5, use version
5.0.*
- If you are using .NET6, use version
6.0.*
Then, in your Startup.cs
, inside ConfigureServices()
method change this:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddControllers();
// ...
}
To this:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddControllers().AddNewtonsoftJson();
// ...
}
Since you just want the object returned as-is, change your code to this:
public async Task<object> GetItemAsync(string id)
{
try
{
var response = await this._container.ReadItemAsync<object>(id, new PartitionKey(id));
return response.Resource;
}
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return null;
}
}
No need to use dynamic
. Just use object
.