How to allow configurable deserialization of JObject? - c#

In a .NET Core Console Application I'm calling a REST API that returns JSON. I'm using Newtonsoft.Json.Linq.JObject for deserialization/retrieval of values like this:
string responseJson = await Utils.GetJsonFromApi(apiEndpoint);
dynamic jObject = new Newtonsoft.Json.Linq.JObject();
jObject = NewtonSoft.Json.JsonConvert.DeserializeObject(responseJson);
foreach (var car in jObject.items)
{
string carId = car.id.ToString();
}
If the API developer changes the array variable items to list or the variable id to code, I will have to update my code and recompile/republish.
Is it possible to leverage app settings somehow, like so:
"carAPI":{
"uri": "someuri",
"itemsArrayVariable": "items",
"carIdVariable": "id"
}
And use these settings in my API client something like this:
foreach (var car in jObject.itemsArrayVariable)
{
string carId = car.carIdVariable.ToString();
}

Deserialize into a Dictionary<string, JToken> and dive into your data recursively. How to do so is documented in other Q&As.
But you shouldn't do this, it will make your code unmaintainable. API developers should not make such breaking changes, and you shouldn't prepare your code for in case they do this anyway. It's what API versioning is for.
This solution also isn't going to work if they're going to move the data to a deeper or higher level in the JSON, and so on. You would have to recompile anyway, and they should do a change like that on another endpoint anyway, allowing old clients to keep working.

Related

Transforming JSON template based on JSON values posted into API

I have got a requirement of building an azure function which transforms data in our COSMOS DB based on the value posted by a calling service.
So consider this JSON template
{
"processExecutionId": 1000,
"processId": "$PID",
"parentProcessId": 10,
"objectNameTag": "$ObjectName",
"objectName": "bbps.fx",
"processStartedOn": "$startedOn",
"processCompletedOn": "$CompletedAt",
"processExecutionStatusId": 2,
"configurationPayload": "Actual Config Payload with replaced values by the orchestrator or payload read service",
"objectMetadata": {
"jsonPayLoadRead": {
"messages": "$Message"
},
"writeToDB": "$IsWrite"
}
}
This is something we in COSMOS corresponding to a key.
So when a requester application posts that Key it has a JSON object in its body like this
{
"processStartedOn": "2022-01-25 10:10:32",
"processCompletedOn": "2022-01-25 10:20:25",
"objectMetadata": {
"jsonPayLoadRead": {
"messages": "Data uploaded"
},
"writeToDB": "True"
}
}
So the method posting parameters for the template expecting a response from the API after replacing those variables in the template with the values.
The JSON template is not always the same structure. Otherwise we can atleast define a class and can deserialize into it. So what ever the key posted our service needs to take the corresponding JSON template from COSMOS and do the transformation.
So dont know how to handle it the best way or is there is any way to handle this process within COSMOS itself rather than trying using C#.
Please share your thoughts
You can always add a TemplateType property in your class so that you know the various options you have and based on that you know what class to use, there are also ways to stream from cosmos directly without using classes at all but it depends a little what you are trying to achieve if you just need to hand over to another app or not then you can stream from cosmos without even knowing what the structure is

How to serialize/deserialize dotnet core exceptions for a client library?

In both dotnet core v3 and v5, I noticed that I cannot successfully serialize an Exception. If I try to I get this
This is due to the System.Type on the TargetSite.DeclaryingType property. This is in v5, but in v3 the serliazation just blows up because it hits the max depth of 32 (and increasing to 128 works but still has strange cyclical behaviors).
The situation I have seems pretty straight forward. I have an application exception like FooNotFoundException that I want to catch and handle in my controller like:
public IActionResult Test()
{
try
{
// try to do something
}
catch(FooNotFoundException e)
{
return NotFound(e);
}
}
Then I have a client library that I want to basically look for status code 404 for this endpoint and then deserialize to this exception type so the consumer can handle this exception as well when this endpoint is called. Seems very straight forward and sought after in my opinion, so I'm shocked to not be able to find any native MS documentation on how to properly serialize and deserialize a basic Exception across network boundaries like this without having to fully implement the translation to a custom DTO.
Curious if anyone has found an elegant way to do this in .NET Core v3 or v5?
I was typing this on the comment but it doesn't fit...
If some basic infomation like message, helpLink,.. was a few desired things,.. how about build our own response standard ?... like this
// On some upper middleware catching exceptions
if (_environment.IsDevelopment())
{
var exceptionDetail = new Dictionary<string, string>()
{
{"Type", exception.GetType().ToString()},
{"Message", exception.Message},
{"StackTrace", exception.StackTrace}
};
foreach (DictionaryEntry data in exception.Data)
exceptionDetail.TryAdd(data.Key.ToString(), data.Value?.ToString());
responseResult.Error.ExceptionDetail = exceptionDetail;
}
return context.Response.WriteAsync(JsonConvert.SerializeObject(responseResult,
new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
}));
I think it's compact enough for clients like spa to handle some detail

Associate class instance to Session in asp.net core

I'm currently working on a webserver in asp.net core.
I want the server to process the users input and data and am looking for a good solution to save complex Objects for the runtime.
So my first approach was to use Sessions. In Asp.net, sessions used to work like Session["key"] = new ValueObject()
In asp.net core however you can only use the methods SetString, SetInt32 and Set for byte arrays. I found a lot of solutions which basically converted the objects into Json strings. However in my case this isn't possible due to the objects containing other object references and more.
My second idea was to create a list of objects with the SessionId as identifier. Problem with this is that every time I would make request to the server, it needs to go through all existing Sessions to find the matching one, so this would probably drastically increase the time for the request.
So my question is what would be the best way to save user related objects?
Is using Sessions even the best way for solving this problem or am I missing something?
Note: Request are handled by JQuery AJAX, so reloading the page for accessing data is not an option.
You could try using the MemoryCache that can hold any .net type. It is not a problem but given it is a shared structure, it will be shared to all users, so, you have to carefull manage it. To do it, you could use HttpContext.Session.Id to define the keys on the memory cache instance. For sample (pseudo-code I didn't test):
public class HomeController : Controller
{
private IMemoryCache _cache;
public HomeController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}
public async Task<IActionResult> CacheGetOrCreateAsynchronous()
{
string cacheKey = $"{HttpContext.Session.Id}_data";
var cacheEntry = await
_cache.GetOrCreateAsync(cacheKey , entry =>
{
entry.SlidingExpiration = TimeSpan.FromSeconds(3);
return Task.FromResult(DateTime.Now);
});
return View("Cache", cacheEntry);
}
}

HttpContext.Current cannot be serialized

I have a line
string serializedContext = JsonConvert.SerializeObject(HttpContext.Current)
but I am getting exception on this line.
Self referencing loop detected for property 'Context' with type 'System.Web.HttpContext'. Path 'ApplicationInstance'.
I am trying to pass serailized context to web service.
Why that self-referencing loop is detected and what could be the way out?
The Misconception:
Serializing your context and de-serialize it on the other side makes no semantical sense: if you send it to a remote web service, it will have its own http context.
Working around the problem
Instead, what you might want to do is : create a new object with just the data you need to use on the remote web service.
Don't consider it as being a context, think of it as a data transfer object that contains all the things you need to get the job done on the other side.
Additional considerations
You might think it is painful to basically create a new object that contains the same data as your HttpContext, but you can mitigate the tediousness of copying the values from one object to the other by using AutoMapper in order to copy the values from one object to the other without the need to write the code yourself (it is convention-based).
Hope this helps.
You can try :
string serializedContext = JsonConvert.SerializeObject(HttpContext.Current, Formatting.Indented,
new JsonSerializerSettings {
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});

JSON to Knockout serializer on server

I am using Web API in a current project. It returns a json string which is than converted to a knockout-observable by using the knockout-mapping plugin. I am wondering if this could not be done before, on the server side. For example:
Now a GET call to \Product returns the following:
{ Id: 2, Name: "Apple", Categories: ["fruit"] }
What I like to have directly returned by the server:
var data={
Id: ko.observable(2),
Name: ko.observable("Apple"),
Categories: ko.observableArray([
ko.observable("fruit")
])
};
Is there any .NET lib to do this? Or is there any way to extend the JSON.NET lib?
Although you could use an OData formatter, it could get difficult to maintain if your front end changes. You could essentially do the same thing on your front end by creating a viewmodel from your server's JSON:
function ViewModel(data)
{
this.Id = ko.observable(data.Id);
this.Name = ko.observable(data.Name);
this.Categories = ko.observableArray(data.Categories);
}
This way, as pointed out by Shane, you wouldn't have your back end tightly coupled with Knockout. Also, as a little side note to your categories, I wouldn't recommend setting each individual item in your array to an observable unless you need to observe each item.

Categories

Resources