JObject.Parse throws error on valid Json - json.net

Searching on 'Newtonsoft.Json.JsonReaderException: Additional text encountered after finished reading JSON content: {. Path '', ...' finds at least 3 SO questions all of which were traced to invalid Json.
I've tried 3 different validators on:
[{"Imported": "This registration imported on: 06/20/2016"},{"ContactInfoUpdated": " Street Address2: Suite 222 to Shipping Address2: "}]
and all three report it as valid. And yet the runtime error tosses that same 'Additional text encountered...':
if (!string.IsNullOrWhiteSpace(UserComments))
{
JToken addresses;
addresses = JObject.Parse(UserComments).GetValue("CarbonCopy"); //errors here
if (!ReferenceEquals(null, addresses))
{
//stuff
}
}
To establish that there are no unintended characters after the json closes, here's the sql:
UPDATE dbo.[Order] SET UserComments = '[{"Imported": "This registration imported on: 06/20/2016"},{"ContactInfoUpdated": " Street Address2: Suite 222 to Shipping Address2: "}]' WHERE idOrder =121050

With thanks to Brian's prompting I found this post to be very helpful:
Get Value from JSON using JArray
My json is somewhat unusual in that it's an array of dissimilar objects. Instead of a series of name/value pair where Name stays constant, my 'UserComments' field contains pairs where Name can be 'ProfileWasEdited', 'CCRequested', 'Feedback', and so on.
In order to accommodate that type of structure I need to test against the Name property:
var fields = JToken.Parse(UserComments);
var isCC = "";
foreach (JObject content in fields.Children<JObject>())
{
foreach (JProperty prop in content.Properties())
{
if (prop.Name == "CarbonCopy")
isCC = prop.Value.ToString();
}
}
Resharper informs me that I can Linq-ize the above to:
foreach (JProperty prop in fields.Children<JObject>().SelectMany(content => content.Properties().Where(prop => prop.Name == "CarbonCopy")))
{
isCC = prop.Value.ToString();
}

Related

Elastic Search in ASP.NET - using ampersand sign

I'm new to Elastic Search in ASP.NET, and I have a problem which I'm, so far, unable to resolve.
From documentation, I've seen that & sign is not listed as a special character. Yet, when I submit my search ampersand sign is fully ignored. For example if I search for procter & gamble, & sign is fully ignored. That makes quite a lot of problems for me, because I have companies that have names like M&S. When & sign is ignored, I get basically everything that has M or S in it. If I try with exact search (M&S), I have the same problem.
My code is:
void Connect()
{
node = new Uri(ConfigurationManager.AppSettings["Url"]);
settings = new ConnectionSettings(node);
settings.DefaultIndex(ConfigurationManager.AppSettings["defaultIndex"]);
settings.ThrowExceptions(true);
client = new ElasticClient(settings);
}
private string escapeChars(string inStr) {
var temp = inStr;
temp = temp
.Replace(#"\", #"\\")
.Replace(#">",string.Empty)
.Replace(#"<",string.Empty)
.Replace(#"{",string.Empty)
.Replace(#"}",string.Empty)
.Replace(#"[",string.Empty)
.Replace(#"]",string.Empty)
.Replace(#"*",string.Empty)
.Replace(#"?",string.Empty)
.Replace(#":",string.Empty)
.Replace(#"/",string.Empty);
return temp;
}
And then inside one of my functions
Connect();
ISearchResponse<ElasticSearch_Result> search_result;
var QString = escapeChars(searchString);
search_result = client.Search<ElasticSearch_Result>(s => s
.From(0)
.Size(101)
.Query(q =>
q.QueryString(b =>
b.Query(QString)
//.Analyzer("whitespace")
.Fields(fs => fs.Field(f => f.CompanyName))
)
)
.Highlight(h => h
.Order("score")
.TagsSchema("styled")
.Fields(fs => fs
.Field(f => f.CompanyName)
)
)
);
I've tried including analyzers, but then I've found out that they change the way tokenizers split words. I haven't been able to implement changes to the tokenizer.
I would like to be able to have following scenario:
Search: M&S Company Foo Bar
Tokens: M&S Company Foo Bar + bonus is if it's possible to have M S tokens too
I'm using elastic search V5.0.
Any help is more than welcome. Including better documentation than the one found here: https://www.elastic.co/guide/en/elasticsearch/client/net-api/5.x/writing-queries.html.
By default for a text field the analyzer applied is standard analyzer. This analyzer applies standard tokenizer along with lowercase token filter. So when you are indexing some value against that field, the standard analyzer is applied on that value and the resultant tokens are indexed against the field.
Let's understand this by e.g. For the field companyName (text type) let us assume that the value being passed is M&S Company Foo Bar while indexing a document. The resultant tokens for this value after the application of standard analyzer will be:
m
s
company
foo
bar
What you can notice is that not just whitespace but also & is used as delimiter to split and generate the tokens.
When you query against this field and don't pass any analyzer in the search query, it by default apply the same analyzer for search as well which is applied for indexing against the field. Therefore, if you search for M&S it get tokenised to M and S and thus actual search query search for these two tokens instead of M&S.
To solve this, you need to change the analyzer for the field companyName. Instead of standard analyzer you can create a custom analyzer which use whitespace tokenizer and lowercase filter (to make search case insensitive). For this you need to change the setting and mapping as below:
{
"settings": {
"analysis": {
"analyzer": {
"whitespace_lowercase": {
"tokenizer": "whitespace",
"filter": [
"lowercase"
]
}
}
}
},
"mappings": {
"_doc": {
"properties": {
"companyName": {
"type": "text",
"analyzer": "whitespace_lowercase",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
}
Now for the above input the tokens generated will be:
m&s
company
foo
bar
This will ensure that when searching for M&S, & is not ignored.

POST data empty ( or not exist ) when I receive post back from TPV provider

I'm trying to implement a service Redsys payments on my .net website.
The payment is successful (data are sent by post) but when the POST data back to my website ( to confirm the payment ) and i try to retrieve them with:
Request.form string value = [ "name"]
the value is always empty
I tried to count how many are in Request.Form.Keys.Count and always gives me zero values.
In the vendor documentation it indicated that the variables may be collected with Request.form [ "name"] and I called them to ask why I do not get the data and they dont know why...so I'm desperate,
What may be due?
I have reviewed the header I get from the server ( width Request.Headers ) and have the following parameters. HttpMethod:: GET Requestype: GET and contentlength: 0 . My bank tell me that they response POST not GET... so it´s a mistery. May be the error is becouse that sendings are made from a https to a htttp ?
You are receiving a POST without parameters.
The parameters are in the content of the call.
You should read the content and get the values of each parameter:
[System.Web.Http.HttpPost]
public async Task<IHttpActionResult> PostNotification()
{
string body = "";
await
Request.Content.ReadAsStreamAsync().ContinueWith(x =>
{
var result = "";
using (var sr = new StreamReader(x.Result))
{
result = sr.ReadToEnd();
}
body += result;
});
In body you can read the parameters (the order of them can change).

Entity Framework search records with a list of strings?

I have a database of records that each have a title. I want to be able to search through this database with a search string that will be separated into a list or an array.
So for example if I search with "Book Dog", it will search for all titles that have "Book" or "Dog" in the title.
I'm using entity framework and I guess the simplest way to write down what I want to do is
string[] words;
var posts = (from p in ctx.posts
where p.title.contains(words)
select p).ToList();
I've tried using a StringExtension I found online, but I would get the following error
"LINQ to Entities does not recognize the method 'Boolean ContainsAny(System.String, System.String[])' method, and this method cannot be translated into a store expression."
And the extension is
public static bool ContainsAny(this string str, params string[] values)
{
if (!string.IsNullOrEmpty(str) || values.Length > 0)
{
foreach (string value in values)
{
if (str.Contains(value))
return true;
}
}
return false;
}
Are you looking for this?
var posts = (from p in ctx.posts
where words.Any(w => p.title.Contains(w))
select p).ToList();
Is this what you need:
ctx.posts.Where(post => words.Any(word => post.Title.Contains(word)))
I was searching for something like your question (Registration form), but my question was: How to find if the whole name that I have entered (from text box) is the same totally to a record in the database ?
I tried this and worked fine:
db.Users.Any(x => x.UserName.Equals(txtUsername.Text))
Or for your code like this:
ctx.posts.Any(x => x.title.Equals(words))
It may be useful for others.

KendoUI Grid serverpaging

I'm trying to populate a KendoUI grid with JSON data where the server returns the total number of rows along with the data, but I'm having some trouble getting serverPaging to work properly. I create and assign the dataSource of the grid as follows:
var oDS = new kendo.data.DataSource({
schema: {
data: "data",
total: "total"
},
data: self.grdTableData,
serverPaging: true,
pageSise: 50,
total: joOutput["TotalRecords"]
});
grdTableResults.setDataSource(oDS);
and the first page shows the first 50 of 939 records but there is only ever 1 page (the navigation arrows never respond to anything) and I see NaN - NaN of 939 items and the rotating circle of dots in the centre of the grid that never goes away.
One thing that is different in all the examples I've looked at is that my $.ajax call and the the processing of the JSON data in .done doesn't use "transport: read" but I'm thinking how I send the data and get it back shouldn't matter (or does it because every page request is a new server read?). But I don't think I'm doing enough to handle the server paging properly even though it seems I'm setting data source values similar to those set in the example at http://jsfiddle.net/rusev/Lnkug/. Then there's the "take" and "skip" values that I'm not sure about, but I do have "startIndex" and "rowsPerPage" that I'm sending to the server that can be used there. I assume the grid can tell me what page I'm on show I can set my "startIndex" appropriately and if I have an Items per Page" drop down I can reset my "rowsPerPage" value?
Anyway, sorry for all the newbie questions. Any help and suggestions is genuinely appreciated. Thanks!
transport: read
You should be able to use "transport: read" even if you have custom logic by setting the value to a function. I have created a JS Fiddle to demonstrate this functionality.
dataSource: {
serverPaging: true,
schema: {
data: "data",
total: "total"
},
pageSize: 10,
transport: {
read: function(options) {
var data = getData(options.data.page);
options.success(data);
}
},
update: function() {}
}
Your read function contains a parameter that contains the following paging properties: page, pageSize, skip, take. Keep in mind that all transport operations need to be functions if one operation contains a function.
startIndex and rowsPerPage
If your server accepts these parameters, you should be able to submit them in the read function. Create a new ajax call that post customized data
var ajaxPostData = { startIndex: options.data.skip, rowsPerPage: options.data.pageSize }
This is the code for server side wrapper that I'm using to implement server paging with kendo grid:
#(Html.Kendo().Grid<IJRayka.Core.Utility.ViewModels.ProductDto>()
.Name("productList")
.Columns(columns =>
{
columns.Bound(prod => prod.Name);
columns.Bound(ord => ord.Brand);
columns.Bound(ord => ord.UnitPackageOption);
columns.Bound(ord => ord.CategoryName);
columns.Bound(ord => ord.Description);
})
.Pageable(pager => pager.PageSizes(true))
.Selectable(selectable => selectable.Mode(GridSelectionMode.Multiple))
.PrefixUrlParameters(false)
.DataSource(ds => ds.Ajax()
.Model(m => m.Id(ord => ord.ID))
.PageSize(5)
.Read(read => read
.Action("FilterProductsJson", "ProductManagement")
.Data("getFilters"))
)
)
Where getFilters is a javascript function that passes my custom filter parameters to the grid when it wants to get data from url/service:
function getFilters() {
return {
brand: $("#Brand").val(),
name: $("#Name").val(),
category: $("#CategoryName").val()
};
}
In addition you should implement your controller's action method using kendo's DataSourceRequest class like below, or otherwise it won't work the way you want:
public JsonResult FilterProductsJson([DataSourceRequest()] DataSourceRequest request,
// three additional paramerters for my custom filtering
string brand, string name, string category)
{
int top = request.PageSize;
int skip = (request.Page - 1) * top;
if(brand != null)
brand = brand.Trim();
if(name != null)
name = name.Trim();
if(category != null)
category = category.Trim();
var searchResult = new ManageProductBiz().GetPagedFilteredProducts(brand, name, category, top, skip);
// remove cyclic references:
searchResult.Items.ForEach(prd => prd.Category.Products = null);
return Json(new DataSourceResult { Data = searchResult.Items, Total = searchResult.TotalCount }, JsonRequestBehavior.AllowGet);
}

In MVC app using JQGrid - how to set user data in the controller action

How do you set the userdata in the controller action. The way I'm doing it is breaking my grid. I'm trying a simple test with no luck. Here's my code which does not work. Thanks.
var dataJson = new
{
total =
page = 1,
records = 10000,
userdata = "{test1:thefield}",
rows = (from e in equipment
select new
{
id = e.equip_id,
cell = new string[] {
e.type_desc,
e.make_descr,
e.model_descr,
e.equip_year,
e.work_loc,
e.insp_due_dt,
e.registered_by,
e.managed_by
}
}).ToArray()
};
return Json(dataJson);
I don't think you have to convert it to an Array. I've used jqGrid and i just let the Json function serialize the object. I'm not certain that would cause a problem, but it's unnecessary at the very least.
Also, your user data would evaluate to a string (because you are sending it as a string). Try sending it as an anonymous object. ie:
userdata = new { test1 = "thefield" },
You need a value for total and a comma between that and page. (I'm guessing that's a typo. I don't think that would compile as is.)
EDIT:
Also, i would recommend adding the option "jsonReader: { repeatitems: false }" to your javascript. This will allow you to send your collection in the "rows" field without converting it to the "{id: ID, cell: [ data_row_as_array ] }" syntax. You can set the property "key = true" in your colModel to indicate which field is the ID. It makes it a lot simpler to pass data to the grid.

Resources