Return multiple objects from an action - c#

I want to fetch data from an Oracle database with Dapper ORM in .NET 6.0.
This is the code to fetch the data:
[HttpGet]
[Route("api/GetDatas")]
public object GetDatas(string? nom = null, string? pre = null)
{
var queryResult = userRepository.GetDatas(nom, pre);
var totalResult = userRepository.GetDatasFullQueryResult(nom, pre);
int timeStamp = (int)(DateTime.UtcNow.Subtract(new DateTime(1970, 1,1))).TotalSeconds;
if (queryResult == null)
{
return NotFound(Tools.returnError("erreur lors de la récuperation des flux avocats, endpoint: api/GetDatas"));
}
return Ok(queryResult); // here my problem
}
I have result in each variable (queryResult, totalResult, timeStamp) and I want to construct a json response like this:
{
"metadata":
[
{
"totalResult": "2",
"timestamp": "1673347991"
}
],
"fiche_avocats":
[
{
"nom": "doe",
"pre": "john"
}
],
[
{
"nom": "foo",
"pre": "bar"
}
]
}
I tried to use a Tuple native type, but the result is like item1: [1673347991]..
Thanks a lot for helping me!
Thery

you can use this solution :
[HttpGet]
[Route("api/GetDatas")]
public object GetDatas()
{
List<object> listObj = new List<object>();
var queryResult = ....................;
var totalResult = ....................;
int timeStamp =.......................;
listObj.Add(queryResult);
listObj.Add(totalResult);
listObj.Add(timeStamp);
return Json(listObj);
}

Related

MongoDB serializing ObjectId to string

Consider the following piece of code
public string sector_get(string id)
{
var client = new MongoClient(connectionstring);
var db = client.GetDatabase("databasename");
var sectorsColl = db.GetCollection<BsonDocument>("sectors");
try
{
BsonDocument sectorObj = sectorsColl.Find(Builders<BsonDocument>.Filter.Eq<ObjectId>("_id", ObjectId.Parse(id))).FirstOrDefault;
return sectorObj.ToJson();
}
catch (Exception ex)
{
throw;
}
}
The function returns the following :
{
"_id": ObjectId("5eb0f25d70a5a75108a2eb5d"),
"title": "Supermarkets",
"subTitle": "Grocerie made easy",
"image": "images/sector/supermarket.jpg",
"displayOrder": 1,
"flatList": 2
}
I need to serialize the string value of "_id" instead of the ObjectId value (as presented below).
{
"_id": "5eb0f25d70a5a75108a2eb5d",
"title": "Supermarkets",
"subTitle": "Grocerie made easy",
"image": "images/sector/supermarket.jpg",
"displayOrder": 1,
"flatList": 2
}
Please note that creating a class based data model, with attribute serialization decoration is not an option.
Your help is appreciated.

How to deserialize JSON data using System.Text.Json

This time I am needing some help deserializing some data that comes from a Web API using using System.Text.Json. After searching all over the place I had found nothing that can really help me solving this issue
Here is a sample of the data:
For the UF Indicator:
{
"UFs": [
{
"Valor": "30.008,40",
"Fecha": "2021-09-10"
}
]
}
For USD Indicator:
{
"Dolares": [
{
"Valor": "791,28",
"Fecha": "2021-09-10"
}
]
}
For UTM Indicator:
{
"UTMs": [
{
"Valor": "52.631",
"Fecha": "2021-09-01"
}
]
}
And a sample for USD with multiple sets of data:
{
"Dolares": [
{
"Valor": "767,10",
"Fecha": "2021-09-02"
},
{
"Valor": "768,36",
"Fecha": "2021-09-03"
},
{
"Valor": "766,53",
"Fecha": "2021-09-06"
},
{
"Valor": "770,33",
"Fecha": "2021-09-07"
},
{
"Valor": "777,94",
"Fecha": "2021-09-08"
},
{
"Valor": "787,51",
"Fecha": "2021-09-09"
},
{
"Valor": "791,28",
"Fecha": "2021-09-10"
}
]
}
This is the class I need to Deserialize to:
public class Indicador : IIndicador
{
public string Valor { get; set; }
public string Fecha { get; set; }
}
The issue starts when I try to Deserialize using this:
var dataFromApi = await httpResponse.Content.ReadAsStringAsync();
var indicador = JsonSerializer.Deserialize<Indicador>(dataFromApi);
I also tried using this "solution" but with no luck at all:
var dataFromApi = await httpResponse.Content.ReadAsStringAsync();
var indicador = JsonSerializer.Deserialize<Dictionary<string,List<indicador>>>(dataFromApi);
So far the only solution has been to create a "container" class that can help me handle that odd "UFs" as a list, since this WebAPI can return multiple other economics indicators than "UFs" which can change to a lot of other concepts, is there a way to map what ever comes from the WebAPI to my generic Indicador class?, the idea is to use a simple object when there is just 1 data and an IEnumerable when there are more than one. I need to stop the dependency for each type of indicator available.
Concerns / Areas of Improvement
Your final paragraph is a little vague and you don't have anymore examples. As such the answer might not be quite as specific as you are looking for.
multiple other economics indicators than "UFs"
Would be nice to see some other examples.
map what ever comes from the WebAPI to my generic Indicador class
Potentially possible, but it remains to be seen how the other data is structured.
Simplest Example
I think you are missing this part of the deserialization process
List<Indicador> indicadors = dataDict["UFs"]; // <-- Will fail If "UFs" is not present
Below is a top level C# program to how to deserialize the given data to a List<Indicador>.
using System;
using System.Text.Json;
using System.Collections.Generic;
// test data
string dataFromApi = #"{
""UFs"": [
{
""Valor"": ""30.008,40"",
""Fecha"": ""2021-09-10""
},
{
""Valor"": ""40.008,50"",
""Fecha"": ""2021-10-10""
}
]
}";
var dataDict = JsonSerializer.Deserialize<Dictionary<string, List<Indicador>>>(dataFromApi);
List<Indicador> indicadors = dataDict["UFs"]; // <-- Will fail If "UFs" is not present
// print out the indicadors
indicadors.ForEach(indicador => Console.WriteLine(indicador));
// Using records because they are brief and come with a good default ToString() method
// You can use regular class if you require
public abstract record IIndicador(string Valor, string Fecha);
public record Indicador(string Valor, string Fecha): IIndicador(Valor, Fecha);
The output of the above top level C# program
Indicador { Valor = 30.008,40, Fecha = 2021-09-10 }
Indicador { Valor = 40.008,50, Fecha = 2021-10-10 }
If "UFs" key is not guaranteed then you can use one of the Dictionary methods to determine if the key is present. For example
if (dataDict.ContainsKey("UFs")) ...
Speculation
Going out on a limb here trying to address some of the aspects of your last paragraph. (You will need to clarify if this address all your concerns and adapt to meet your needs) System.Text.Json also has JsonConverterFactory and JsonConverter<T> for more advanced Conversion requirements should you need them.
using System;
using System.Linq;
using System.Text.Json;
using System.Collections.Generic;
// test data
string multiDataFromApi = #"{
""UFs"": [
{
""Valor"": ""30.008,40"",
""Fecha"": ""2021-09-10""
},
{
""Valor"": ""40.008,50"",
""Fecha"": ""2021-10-10""
}
],
""UFOs"": [
{
""Valor"": ""30.008,40"",
""Fecha"": ""2021-09-10""
}
]
}";
string singleDataFromApi = #"{
""UFs"": [
{
""Valor"": ""30.008,40"",
""Fecha"": ""2021-09-10""
},
{
""Valor"": ""40.008,50"",
""Fecha"": ""2021-10-10""
}
]
}";
processDataFromApi(multiDataFromApi);
processDataFromApi(singleDataFromApi);
void processDataFromApi(string json)
{
var dataDict = JsonSerializer.Deserialize<Dictionary<string, List<Indicador>>>(json);
if (dataDict.Count == 1)
{
Console.WriteLine("-- Single Key Processing --");
List<Indicador> indicadors = dataDict.Values.First();
indicadors.ForEach(indicador => Console.WriteLine("\t{0}", indicador));
}
else
{
Console.WriteLine("-- Multi Key Processing --");
foreach (var keyValuePair in dataDict)
{
Console.WriteLine($"Processing Key: {keyValuePair.Key}");
List<Indicador> indicadors = keyValuePair.Value;
indicadors.ForEach(indicador => Console.WriteLine("\t{0}",indicador));
}
}
Console.WriteLine("-----------------------------");
}
// Using records because they are brief and come with a good default ToString() method
// You can use regular class if you require
public abstract record IIndicador(string Valor, string Fecha);
public record Indicador(string Valor, string Fecha): IIndicador(Valor, Fecha);
which will produce the following output
-- Multi Key Processing --
Processing Key: UFs
Indicador { Valor = 30.008,40, Fecha = 2021-09-10 }
Indicador { Valor = 40.008,50, Fecha = 2021-10-10 }
Processing Key: UFOs
Indicador { Valor = 30.008,40, Fecha = 2021-09-10 }
-----------------------------
-- Single Key Processing --
Indicador { Valor = 30.008,40, Fecha = 2021-09-10 }
Indicador { Valor = 40.008,50, Fecha = 2021-10-10 }
-----------------------------

Deserialize JSON with variable property name and nested list to object

I have a bunch of API which generates the following 2 kinds of reply in response body:
{ "error": null, "object_type": { /* some object */ } }
{ "error": null, "object_type": [{ /* some object */ }, { /* some object */ }, ...] }
While I have a class corresponding to the object structure, I want to deserialize the API endpoints directly into either an object of the class or a List<class>, without creating some "result" classes to match the response JSON structure. Is this possible?
For example there is 2 API:
/api/getAllCompanies
returns
{ "error": null, "company": [ { "name": "Microsoft", "country": "US" }, { "name": "Apple", "country": "US" } ]
while
/api/getUserCompany
returns
{ "error": null, "company": { "name": "Microsoft", "country": "US" } }
I have a class in code:
public class Company {
string Name { get; set; }
string Country { get; set; }
}
How can I directly deserialize the data into a Company object or a List<Company> without creating a bunch of other class?
(The JSON property name (company) is known so don't need to extract it elsewhere.)
I've been trying to first deserialize the response JSON into an ExpandoObject then copy the properties to an instance of destination class using the code here then convert it using the following code, but this seems not to work with lists.
private static async Task<T> GetApiObject<T>(string api, string extractedObjName) where T: class, new()
{
var retstr = await /* get API response as string */;
dynamic retobj = JsonConvert.DeserializeObject<ExpandoObject>(retstr, new ExpandoObjectConverter());
var ret = new T();
Mapper<T>.Map((ExpandoObject)((IDictionary<string, object>)retobj)[extractedObjName], ret);
return ret;
}
You can use JObejct to extract the information you need before deserialize it into the object.
var str = "{ \"error\": null, \"company\": [{ \"name\": \"Microsoft\", \"country\": \"US\" } ,{ \"name\": \"Apple\", \"country\": \"US\" } ]}";
var temp = JObject.Parse(str).GetValue("company");
var companies = temp.Select(x => x.ToObject<Company>()).ToList();
Same goes for /api/getUserCompany
var str = "{ \"error\": null, \"company\": { \"name\": \"Microsoft\", \"country\": \"US\" } }";
var temp = JObject.Parse(str).GetValue("company");
var company = temp.ToObject<Company>();

Using Facets in the Aggregation Framework C#

I would like to create an Aggregation on my data to get the total amount of counts for specific tags for a collection of books in my .Net application.
I have the following Book class.
public class Book
{
public string Id { get; set; }
public string Name { get; set; }
[BsonDictionaryOptions(DictionaryRepresentation.Document)]
public Dictionary<string, string> Tags { get; set; }
}
And when the data is saved, it is stored in the following format in MongoDB.
{
"_id" : ObjectId("574325a36fdc967af03766dc"),
"Name" : "My First Book",
"Tags" : {
"Edition" : "First",
"Type" : "HardBack",
"Published" : "2017",
}
}
I've been using facets directly in MongoDB and I am able to get the results that I need by using the following query:
db.{myCollection}.aggregate(
[
{
$match: {
"Name" : "SearchValue"
}
},
{
$facet: {
"categorizedByTags" : [
{
$project :
{
Tags: { $objectToArray: "$Tags" }
}
},
{ $unwind : "$Tags"},
{ $sortByCount : "$Tags"}
]
}
},
]
);
However I am unable to transfer this over to the .NET C# Driver for Mongo. How can I do this using the .NET C# driver?
Edit - I will ultimately be looking to query the DB on other properties of the books as part of a faceted book listings page, such as Publisher, Author, Page count etc... hence the usage of $facet, unless there is a better way of doing this?
I would personally not use $facet here since you've only got one pipeline which kind of defeats the purpose of $facet in the first place...
The following is simpler and scales better ($facet will create one single potentially massive document).
db.collection.aggregate([
{
$match: {
"Name" : "My First Book"
}
}, {
$project: {
"Tags": {
$objectToArray: "$Tags"
}
}
}, {
$unwind: "$Tags"
}, {
$sortByCount: "$Tags"
}, {
$group: { // not really needed unless you need to have all results in one single document
"_id": null,
"categorizedByTags": {
$push: "$$ROOT"
}
}
}, {
$project: { // not really needed, either: remove _id field
"_id": 0
}
}])
This could be written using the C# driver as follows:
var collection = new MongoClient().GetDatabase("test").GetCollection<Book>("test");
var pipeline = collection.Aggregate()
.Match(b => b.Name == "My First Book")
.Project("{Tags: { $objectToArray: \"$Tags\" }}")
.Unwind("Tags")
.SortByCount<BsonDocument>("$Tags");
var output = pipeline.ToList().ToJson(new JsonWriterSettings {Indent = true});
Console.WriteLine(output);
Here's the version using a facet:
var collection = new MongoClient().GetDatabase("test").GetCollection<Book>("test");
var project = PipelineStageDefinitionBuilder.Project<Book, BsonDocument>("{Tags: { $objectToArray: \"$Tags\" }}");
var unwind = PipelineStageDefinitionBuilder.Unwind<BsonDocument, BsonDocument>("Tags");
var sortByCount = PipelineStageDefinitionBuilder.SortByCount<BsonDocument, BsonDocument>("$Tags");
var pipeline = PipelineDefinition<Book, AggregateSortByCountResult<BsonDocument>>.Create(new IPipelineStageDefinition[] { project, unwind, sortByCount });
// string based alternative version
//var pipeline = PipelineDefinition<Book, BsonDocument>.Create(
// "{ $project :{ Tags: { $objectToArray: \"$Tags\" } } }",
// "{ $unwind : \"$Tags\" }",
// "{ $sortByCount : \"$Tags\" }");
var facetPipeline = AggregateFacet.Create("categorizedByTags", pipeline);
var aggregation = collection.Aggregate().Match(b => b.Name == "My First Book").Facet(facetPipeline);
var output = aggregation.Single().Facets.ToJson(new JsonWriterSettings { Indent = true });
Console.WriteLine(output);

Search for a nested value inside of a JSON.net object in C#

I've got a JSON stream coming back from a server, and I need to search for a specific value of the node "ID" using JSON.net to parse the data.
And I can almost make it work, but not quite because the results coming back are deeply nested in each other -- this is due to the fact that I'm getting a folder structure back. I've boiled the JSON down to a much simpler version. I'm getting this:
{
"data": {
"id": 0,
"name": "",
"childFolders": [{
"id": 19002,
"name": "Locker",
"childFolders": [{
"id": 19003,
"name": "Folder1",
"childFolders": [],
"childComponents": [{
"id": 19005,
"name": "route1",
"state": "STOPPED",
"type": "ROUTE"
}]
}, {
"id": 19004,
"name": "Folder2",
"childFolders": [],
"childComponents": [{
"id": 19008,
"name": "comm1",
"state": "STOPPED",
"type": "COMMUNICATION_POINT"
}, {
"id": 19006,
"name": "route2",
"state": "STOPPED",
"type": "ROUTE"
}, {
"id": 19007,
"name": "route3",
"state": "STOPPED",
"type": "ROUTE"
}]
}],
"childComponents": []
}],
"childComponents": []
},
"error": null
}
I can almost get there by going:
var objects = JObject.Parse(results);
var subobjects = objects["data"]["childFolders"][0]["childFolders"][1];
I can see in the debug view that it'll parse the object, but won't let me search within.
My ultimate goal is to be able to search for "route3" and get back 19007, since that's the ID for that route. I've found some results, but all of them assume you know how far nested the object is. The object I'm searching for could be 2 deep or 20 deep.
My ultimate goal is to be able to search for "route3" and get back 19007
You can use linq and Descendants method of JObject to do it:
var dirs = JObject.Parse(json)
.Descendants()
.Where(x=>x is JObject)
.Where(x=>x["id"]!=null && x["name"]!=null)
.Select(x =>new { ID= (int)x["id"], Name = (string)x["name"] })
.ToList();
var id = dirs.Find(x => x.Name == "route3").ID;
You can use the SelectToken or SelectTokens functions to provide a JPath to search for your desired node. Here is an example that would provide you the route based on name:
JObject.Parse(jsonData)["data"].SelectToken("$..childComponents[?(#.name=='route3')]")
You can find more documentation on JPath here
Simply write a recursive function:
private Thing FindThing(Thing thing, string name)
{
if (thing.name == name)
return thing;
foreach (var subThing in thing.childFolders.Concat(thing.childComponents))
{
var foundSub = FindThing(subThing, name);
if (foundSub != null)
return foundSub;
}
return null;
}
class RootObject
{
public Thing data { get; set; }
}
class Thing
{
public int id { get; set; }
public string name { get; set; }
public List<Thing> childFolders { get; set; } = new List<Thing>();
public List<Thing> childComponents { get; set; } = new List<Thing>();
}
And using it:
var obj = JsonConvert.DeserializeObject<RootObject>(jsonString);
var result = FindThing(obj.data, "route3");

Categories

Resources