Working in a C# Asp.netcore project, I'm trying to read an array from an IQueryCollection in a GET request. The IQueryCollection is in Request.Query.
I need to read the query without a model. When the front end json hits the back end, its no longer JSON which is fine, I just need to read whatever the front end passed. In this example its a int array but it could be anything.
Models don't scale very well for queries. To return a model on a get, they work brilliantly but for a query that can be as complex or simple as it needs to be I can't commit to models. Would be really usefull if I could extract the query to an anonamous object.
Json passed from the front end:
var params = {
items: [1, 4, 5],
startDate: new Date(),
endDate: new Date()
}
C# Request.QueryString
{?items%5B%5D=1&items%5B%5D=4&items%5B%5D=5&startDate=Fri%20Oct%2015%202021%2022%3A30%3A57%20GMT%2B1000%20(Australian%20Eastern%20Standard%20Time)&endDate=Fri%20Oct%2015%202021%2022%3A30%3A57%20GMT%2B1000%20(Australian%20Eastern%20Standard%20Time)}
I've tried:
// Gives me a null
var value = HttpUtility.ParseQueryString(Request.QueryString.Value).Get("items");
// Gives me an empty object {}
var value = Request.Query["items"];
Hope this is enough information.
The query string format is undefined for arrays. Some web frameworks use ?foo[]=1&foo[]=2, others use ?foo=1&foo=2, again others ?foo[0]=1&foo[1]=2.
You'll have to use the same parsing serverside as you serialize it clientside. This works for your [] syntax:
var queryString = "?items%5B%5D=1&items%5B%5D=4&items%5B%5D=5&date=2021-10-15";
var parsed = HttpUtility.ParseQueryString(queryString);
foreach (var key in parsed.AllKeys)
{
if (key.EndsWith("[]"))
{
var values = string.Join(", ", parsed.GetValues(key));
Console.WriteLine($"{key}: array: {values}.");
}
else
{
Console.WriteLine($"{key}: scalar: {parsed[key]}.");
}
}
Output:
items[]: array: 1, 4, 5.
date: scalar: 2021-10-15.
But instead of parsing the query string yourself, let the framework do that. You say you don't find models scalable; I find that hand-crafting code doesn't scale well. A model like this would just work, granted you fix your date serializer on the JS side:
public class RequestModel
{
public int[] Items { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
Related
Here's a simplified example. I want to pull in JSON data where the following are options:
{
"cost": 5
}
or
{
"cost": { "base": 5, "inc": 2}
}
Right now I'm using JsonUtility.FromJson<MyObject> which forces me to choose a single type. I am willing to convert one format to the other, so {"cost": 5} can become:
class MyObject
{
MyEntry cost = new MyEntry { #base = 5 };
}
Is there a way to do this using JsonUtility or another parser?
If you don't know or can't guarantee your object layout, then you'll have to use the dynamic mode.
var parsed = JObject.Parse(jsonString)
From here, you can call Value to try and get your data out.
var data = json.Value<string>("data")
// or
var data = json.Value<MyEntry>("data")
That should get you started.
apologies if I'm doing something wrong, this is my first post.
I'm currently working with C# and want to save a bunch of data out to a JSON file and load it back, but I'm having trouble figuring out how to get it in the following format.
// Primary ID
001
{
// Secondary ID
01
{
// Tertiary ID
01
{
string: "this is some information.",
int: 9371
}
}
// Secondary ID
02
{
// Tertiary ID
01
{
string: "blah blah blah.",
int: 2241
}
}
}
I'd essentially like to be able to call up information with a particular set of IDs for example 001-02-01 which would return a string ("blah blah blah.") and an int (2241).
The reason I want to go about it like this instead of just having one longer ID is so that when the JSON file becomes very large, I'm hoping to be able to speed up the search for information by passing each ID in turn.
If that makes no sense and it would be equally as fast to just pass in one longer ID and not be bothered by this whole nested ID segments concept then please let me know!
If, however what I'm thinking is correct and it would help the speed of finding particular data by structuring it out like this, how would I go about doing that? With nested C# classes in arrays?
The most simple way and efficient way would be to have all data as same type. Currently, you seem to go for each object is of type of the given id:
{
"01":{},
"02" :{}
}
this will not go too well if trying to use a serializable class.
I would recommend the following:
{
"items" : [
{"id":"01" }, { "id":"02" },...
]
}
Then you can serialize/deserialize easily with
[Serializable]
public class Item
{
public string id = null;
}
[Serializable]
public class RootObject
{
public List<Item> items = null;
}
and then in Unity:
void Start(){
string str = GetJson(); // However you get it
RootObject ro = JsonUtility.FromJson<RootObject>(str);
}
if you want to speed up the fetching and your collection is large, convert to dictionary.
Dictionary<string, Item> dict = null;
void Start(){
string str = GetJson(); // However you get it
RootObject ro = JsonUtility.FromJson<RootObject>(str);
this.dict = new Dictionary<string,Item>();
foreach(Item item in ro.items){
Item temp = temp;
this.dict.Add(item.Id, temp);
}
ro = null;
}
Now you can access real fast.
Item GetItem(string id)
{
if(string.IsNullOrEmpty(id) == true){ return null; }
Item item = null;
this.dict.TryGetValue(id, out item);
return item;
}
If you end up storing millions of records in your file and want to start doing something more performant it would be easier to switch to a decent document database like MongoDB rather than trying to reinvent the wheel.
Worry about writing good standard code before worrying about performance problems that don't yet exist.
The following example is not in your language of choice but it does explain that JSON and arrays of 1,000,000 objects can be searched very quickly:
const getIncidentId = () => {
let id = Math.random().toString(36).substr(2, 6).toUpperCase().replace("O", "0")
return `${id.slice(0, 3)}-${id.slice(3)}`
}
console.log("Building array of 1,000,000 objects")
const littleData = Array.from({ length: 1000000 }, (v, k) => k + 1).map(x => ({ cells: { Number: x, Id: getIncidentId() } }))
console.log("Getting list of random Ids for array members [49, 60, 70000, 700000, 999999]")
const randomIds = ([49, 60, 70000, 700000, 999999]).map(i => littleData[i].cells.Id)
console.log(randomIds)
console.log("Finding each array item that contains a nested Id property in the randomIds list.")
const foundItems = littleData.filter(i => randomIds.includes(i.cells.Id))
console.log(foundItems)
I am new to C# and to RestSharp.
I am writing a small program to retrieve a list of records via REST. I have been able to retrieve one record. Now I need to get a list of records, and here I have a problem.
The response I get using SoapUI looks like this:
{
"#count": 2,
"#start": 1,
"#totalcount": 2,
"Messages": [],
"ResourceName": "email",
"ReturnCode": 0,
"content": [
{"email": {"evsysseq": "0000000000000262"}},
{"email": {"evsysseq": "0000000000000263"}}
]
}
My code looks like this:
class EmailID
{
public string Evsysseq { get; set; }
}
var client = new RestClient("xxxxx");
client.Authenticator = new HttpBasicAuthenticator("xxx", "xxx");
string queryParm = HttpUtility.UrlEncode("evsysseq>\"0000000000000261\"");
var request = new RestRequest("xxxx?query="+ queryParm, Method.GET);
request.RootElement = "content";
var queryResult = client.Execute<List<EmailID>>(request).Data;
Running it does not result in errors, and I can see on the queryResult object that it does contain two records. But, Evsysseq is null on both, and that is my problem. I am not sure what to tweak to get it right.
You are getting null values because the JSON you are deserializing does not match the class structure you are deserializing into. You are telling RestSharp to deserialize the content array into a List<EmailID>, but the JSON really represents a list of objects that contain EmailID objects. And so you need another class:
class EmailObj
{
public EmailID Email { get; set; }
}
Then deserialize like this and you should get the data:
var queryResult = client.Execute<List<EmailObj>>(request).Data;
If you want to, you can then use LINQ to get the List<EmailID> that you originally wanted like this:
var emailIds = queryResult.Select(eo => eo.Email).ToList();
HTH
I have a User class that accumulates lots of DataTime entries in some List<DateTime> Entries field.
Occasionally, I need to get last 12 Entries (or less, if not reached to 12). It can get to very large numbers.
I can add new Entry object to dedicated collection, but then I have to add ObjectId User field to refer the related user.
It seems like a big overhead, for each entry that holds only a DateTime, to add another field of ObjectId. It may double the collection size.
As I occasionally need to quickly get only last 12 entries of 100,000 for instance, I cannot place these entries in a per-user collection like:
class PerUserEntries {
public ObjectId TheUser;
public List<DateTime> Entries;
}
Because it's not possible to fetch only N entries from an embedded array in a mongo query, AFAIK (if I'm wrong, it would be very gladdening!).
So am I doomed to double my collection size or is there a way around it?
Update, according to #profesor79's answer:
If your answer works, that will be perfect! but unfortunately it fails...
Since I needed to filter on the user entity as well, here is what I did:
With this data:
class EndUserRecordEx {
public ObjectId Id { get; set; }
public string UserName;
public List<EncounterData> Encounters
}
I am trying this:
var query = EuBatch.Find(u => u.UserName == endUser.UserName)
.Project<BsonDocument>(
Builders<EndUserRecordEx>.Projection.Slice(
u => u.Encounters, 0, 12));
var queryString = query.ToString();
var requests = await query.ToListAsync(); // MongoCommandException
This is the query I get in queryString:
find({ "UserName" : "qXyF2uxkcESCTk0zD93Sc+U5fdvUMPow" }, { "Encounters" : { "$slice" : [0, 15] } })
Here is the error (the MongoCommandException.Result):
{
{
"_t" : "OKMongoResponse",
"ok" : 0,
"code" : 9,
"errmsg" : "Syntax error, incorrect syntax near '17'.",
"$err" : "Syntax error, incorrect syntax near '17'."
}
}
Update: problem identified...
Recently, Microsoft announced their DocumentDB protocol support for MongoDB. Apparently, it doesn't support yet all projection operators. I tried it with mLab.com, and it works.
You can use PerUserEntries as this is a valuable document structure.
To get part of that array we need to add projection to query, so we can get only x elements and this is done server side.
Please see snippet below:
static void Main(string[] args)
{
// To directly connect to a single MongoDB server
// or use a connection string
var client = new MongoClient("mongodb://localhost:27017");
var database = client.GetDatabase("test");
var collection = database.GetCollection<PerUserEntries>("tar");
var newData = new PerUserEntries();
newData.Entries = new List<DateTime>();
for (var i = 0; i < 1000; i++)
{
newData.Entries.Add(DateTime.Now.AddSeconds(i));
}
collection.InsertOne(newData);
var list =
collection.Find(new BsonDocument())
.Project<BsonDocument>
(Builders<PerUserEntries>.Projection.Slice(x => x.Entries, 0, 3))
.ToList();
Console.ReadLine();
}
public class PerUserEntries
{
public List<DateTime> Entries;
public ObjectId TheUser;
public ObjectId Id { get; set; }
}
I am trying to serialize a list to json string using Json.NET but the return string has backslash within it, which in turn is failing a json parsing.
var x = from d in entities.Books.ToList()
select new
{
ID = d.ID,
BookName = d.BookName
};
return JsonConvert.SerializeObject(x.ToList());
The above code returns
"[{\"ID\":1,\"BookName\":\"MVC Music Store - Tutorial - v3.0\"},{\"ID\":2,\"BookName\":\"Pro.ASP.NET.MVC.3.Framework\"},{\"ID\":3,\"BookName\":\"Application Architecture Guide v2\"},{\"ID\":4,\"BookName\":\"Gang of Four Design Patterns\"},{\"ID\":5,\"BookName\":\"CS4 Pocket Reference\"}]"
which fails all JSON parsing. How can I remove these.
No. it doesn't
class Program
{
class Book
{
public int ID;
public string BookName;
}
static void Main()
{
var books = new List<Book> { new Book { ID = 1, BookName = "A" }, new Book { ID = 2, BookName = "B" } };
var x = from d in books
select new
{
ID = d.ID,
BookName = d.BookName
};
string str = JsonConvert.SerializeObject(x.ToList());
Console.WriteLine(str);
}
}
There could be two problems:
A) You are looking at the result from the debugger. To check for this, Put the JsonConvert in a temporary variable (like I did) and look at it with the debugger. Click on the arrow right of the hourglass and select Text Visualizer.
or
B) The calling method is transforming the object again to Json, so escaping everything.
string str = "Your string with slashes";
str = JToken.Parse({your string here}).ToString();
The JSON object is serialized twice.
I solved by:
Declaring the operation contract of the method response format to return JSON.
I changed the method to return an object instead of a string.
The serializing of Jason will be done automatically behind the scenes.
I was getting the same result, but with doubled escape shashes while I was unit testing some json serialization. Looking at my code I realized I am serializing the "expected" json string instead of the actual .net object. So, passing a json string to JsonConvert.SerializeObject(expectedJsonString) will simply escape it once over. This is how I came here, and this is the answer I wrote, when I realized I just did a coding mistake... Did you just realize yours?