Attempting simple proof of concept of microsoft / RulesEngine but getting JsonSerializationException - c#

I'm assuming I've made a stupid mistake here, but wondering if someone could assist?
I am trying out the following library:
Install-Package RulesEngine -Version 3.2.0
My rules.json is as follows:
[
{
"WorkflowName": "DeliverToMatchingSuburb",
"Rules": [
{
"RuleName": "MatchingSuburb",
"Expression": "customer.PostCode == location.PostCode"
}
]
}
]
My code:
string[] readText = File.ReadAllLines("rules.json");
Customer c = new Customer();
Pharmacy p = new Pharmacy();
var pharmacies = p.GetLocations();
var customers = c.GenerateCustomers();
var re = new RulesEngine.RulesEngine(readText, null);
My attempt to pass 'readText' into the RulesEngine however gives me the following exception?
Newtonsoft.Json.JsonSerializationException: 'Unexpected end when reading JSON. Path '', line 1, position 1.'
The exception occurs at this line: var re = new RulesEngine.RulesEngine(readText, null);
I have checked that the JSON is valid by using an online JSON
validator.
I've also tried adding curly braces at the top and
bottom.
I've even tried copy/pasting the example rules JSON from the
documentation here
https://github.com/microsoft/RulesEngine#how-to-use-it (knowing
it won't work but should at least pass the step of creating the
RulesEngine) but that too fails with the same error.

The expectation for the RulesEngine(string[], ILogger, ReSettings) constructor is that each element of the string array is a complete JSON object. In your case, you've provided just a single line per array element.
Given that your text file already contains a collection of rules, you should deserialize it yourself, and pass the deserialized collection into the constructor accepting a WorkflowRules[]:
string json = File.ReadAllText("rules.json");
var rules = JsonConvert.DeserializeObject<WorkflowRules[]>(json);
var engine = new RulesEngine.RulesEngine(rules);

Related

Getting error Newtonsoft.Json.Linq.JProperty cannot have multiple values when adding JToken

I have the following structure of additional information where I need to update the value of one of the tokens in the structure. The data is an array of JTokens with a parent called 'additionalFields' as follows:
{{"additionalFields":
[
{ "name": "NAME1", "value": "VALUE1" },
{ "name": "NAME2", "value": "VALUE2" },
{ "name": "NAME3", "value": "VALUE3" },
{ "name": "NAME4", "value": "VALUE4" }
]}
I'm trying to update the value of one of the tokens e.g. to change VALUE1 to VALUE10.
Once I have located the token I need to update my code removes it as follows.
additionalField.Remove();
I then create a new token to replace the one I have removed (containing the new value) using the following functions.
public static JToken CreateNewToken(string name, string value)
{
var stringToken = CreateNewStringToken(name, value);
var token = JToken.Parse(stringToken);
return (JToken) token;
}
private static string CreateNewStringToken(string name, string value)
{
return $"{{\"name\":\"{name}\",\"value\":\"{value}\"}}";
}
I then add the new token as follows.
additionalFields.AddAfterSelf(updatedToken);
Putting it all together we have the following
foreach (var additionalField in additionalFields)
{
//is this the key we are looking for?
var keyToken = additionalField.First;
if (keyToken?.First == null) continue;
if (string.Equals(keyToken.First.ToString(), "newname", StringComparison.CurrentCultureIgnoreCase))
{
//remove the current token
additionalField.Remove();
//add the updated token
var updatedToken = CreateNewToken("newname", "newvalue");
additionalFields.AddAfterSelf(updatedToken); <-- error occurs here!!
}
}
However after adding the token I'm getting the following error
Newtonsoft.Json.Linq.JProperty cannot have multiple values
I can see in the debugger that the token has been removed (as the token.Count is reduced by 1) so cannot understand why I'm getting an error adding the replacement token.
I was able to reproduce your problem here: https://dotnetfiddle.net/JIVCVB
What is going wrong
additionalFields refers to the JArray of JObjects containing name and value JProperties. You are looping through this JArray to try to find the first JObject having a name property with a certain value, and when you find it you attempt to replace the JObject with a whole new JObject. You successfully remove the old JObject from the JArray, but when you are doing AddAfterSelf to insert the new JObject, you are referencing additionalFields (plural) not additionalField (singular). Recall that additionalFields is the JArray. So you are saying that you want to add the new JObject after the array. The array's parent is a JProperty called additionalFields. A JProperty can only have one value, so AddAfterSelf fails with the error you see.
How to fix your code
I think what you intended to do was additionalField.AddAfterSelf(updatedToken). However, this, too, will fail, for a different reason: you already removed the additionalField from the JArray at that point, so it no longer has a parent context. You would need to AddAfterSelf before you remove the item you are trying to insert after. If you fix that, you still have another problem: your loop doesn't break out after you've done the replacement, so then you will get an error about modifying the collection while looping over it.
Here is the relevant section of code with the corrections:
if (string.Equals(keyToken.First.ToString(), "NAME1", StringComparison.CurrentCultureIgnoreCase))
{
//add the updated token
var updatedToken = CreateNewToken("newname", "newvalue");
additionalField.AddAfterSelf(updatedToken);
//remove the current token
additionalField.Remove();
// we found what we were looking for so no need to continue looping
break;
}
Fiddle: https://dotnetfiddle.net/KcFsZc
A simpler approach
You seem to be jumping through a lot of hoops to accomplish this task. Instead of looping, you can use FirstOrDefault to find the object you are looking for in the array. Once you've found it, you don't need to replace the whole object; you can just update the property values directly.
Here's how:
var rootObject = JToken.Parse(json);
// Get a reference to the array of objects as before
var additionalFields = rootObject["additionalFields"];
// Find the object we need to change in the array
var additionalField = additionalFields.FirstOrDefault(f =>
string.Equals((string)f["name"], "NAME1", StringComparison.CurrentCultureIgnoreCase);
// if the object is found, update its properties
if (additionalField != null)
{
additionalField["name"] = "newname";
additionalField["value"] = "newvalue";
}
Working demo: https://dotnetfiddle.net/ZAKRmi

Sending class data as JSON array format for GET request Response in ASP.Net Dot Core Web API ( GET response data from Web API)

I am writing a Web API with requirement where need to pass result class property values as array of Json in response of GET request.
Property class which will be passed as a actual result with Ok Status with object. ( I am mocking actual requirement)
public class ABC
{
public string Name {get;set;}
public string Address{get;set;}
}
I am following default JSONfor matter option which are available in dotnet core web api and it is converting all class attribute into single json element.
{
"Person" :
[
{
"Name": "ABCD",
"Address": "INDIA"
}
]
}
My requirement is to have data in Json format with array as below -
{
"Person" :
[
{"Name": "ABCD"},
{"Address": "INDIA"}
]
}
using Newtonsoft.Json;
use this method to convert obj to string:
JsonConvert.SerializeObject(object)
use this method to convert string to obj:
JsonConvert.DeserializeObject(string)
=== Updated my answer to reflect clarified details ===
Solution with Json.Net:
To get the JSON result that you're looking for, you'll need to create a custom serializer or build your JSON object with dynamic JTokens.
Here's an example using a dynamic JObject:
https://dotnetfiddle.net/EyL5Um
Code:
// Create sample object to serialize
var person = new ABC() { Name = "ABC", Address = "India" };
// Build JSON with dynamic JTokens
dynamic jsonObj = new JObject();
var token = new JArray();
token.Add(new JObject(
new JProperty("Name", person.Name)));
token.Add(new JObject(
new JProperty("Address", person.Address)));
jsonObj.Person = token;
// Print result to console
Console.WriteLine(jsonObj.ToString());
Note
In this form, the code above is not a scalable solution. But it should provide you with a starting point to then build up an iterative approach for the data you're working with.
References
Newtonsoft Documentation - Create JSON w/ Dynamic

JsonConvert.DeserializeAnonymousType definition syntax issue

I have the following code:
var definition = new { result = "", accountinformation = new[] { "" , "" , "" } };
var accountInformationResult = JsonConvert.DeserializeAnonymousType(responseBody, definition);
The account information structure comes back from an endpoint as an array with each element being another array containing 3 strings. So the embedded array is not in a key value pair format. With the above definition accountinformation returns null. What should the syntax be for this structure?
For reference this is what is going on in the php endpoint.
$account_information[] = array( $billing_company, $customer_account_number, $customer_account_manager );
This first line is in a loop. Hence the multi-dimensional array.
echo json_encode(array('result'=>$result, 'account_information'=>$account_information));
I know I could use dynamic but why the extra effort?
I assume your json will look something like this:
{
"result": "the result",
"account_information": [
["company1", "account_number1", "account_manager1"],
["company2", "account_number2", "account_manager2"]
]
}
In that case you should be able to deserialize with the following definition (note the underscore in account_information:
var definition = new { result = "", account_information = new List<string[]>() };
In json you are allowed to add extra properties as you please while your data model changes. So if you define a data model that does not include one of these properties, the property will simple be ignored. In your case the definition does not have a property called account_information (exactly), so this part of the json is ignored while deserializing.
EDIT:
If it's going to be an anonymous abject anyway, you may also consider parsing into a JObject:
var obj = JObject.Parse(responseBody);
string firstCompany = obj["account_information"][0][0];
string secondCompany = obj["account_information"][1][0];

Double Label for Json Property C#

I need to pass a Json object to an API, but the API requires the Json properties to have a double label of sorts, such as:
{
"name:id":"1234"
}
However, using Newtonsoft.Json.Linq, I can't get this to format the label exactly. Here is what I've tried so far (which throws an error)
dynamic json= new JObject();
json.name.id = "1234";
Doing
json.id = "1234";
Works just fine. I have also tried
json.name = new JProperty("id", "1234");
Which also throws an error. I have also tried hard coding the json file as a single string and converting that to a JObject, which also threw an error. Is what I'm trying to do possible or am I missing something? Is there another Json package I could use that would support what I want to do?
Use JObject's string indexer notation.
dynamic json = new JObject();
json["name.id"] = "1234";
Since the json is essentially built as a key/value pair, using a string indexer can allow you to overcome atypical property names.
There multiple ways to achieve that.
You can use JsonProperty attribute and specify the property name as name:id like:
class MyClass
{
[JsonProperty("name:id")]
public string Name_Id { get; set; }
}
and then you can do:
MyClass obj = new MyClass();
obj.Name_Id = "1234";
var strJson = JsonConvert.SerializeObject(obj);
and you will get back:
{"name:id":"1234"}

Get partial object list from JSON C#

I am calling a RESTful service in C# and the result is similar to this:
{
"blabla":1,
"bbb":false,
"blabla2":{
"aaa":25,
"bbb":25,
"ccc":0
},
"I_want_child_list_from_this":{
"total":15226,
"max_score":1.0,
"I_want_this":[
{
"A":"val1",
"B":"val2",
"C":"val3"
},
{
"A":"val1",
"B":"val2",
"C":"val3"
}
...
]
"more_blabla": "fff"
...
}
I want to get the "I_want_this" part as a list of object or JObject
Is there something like
(JObject)responseString["I_want_child_list_from_this"]["I_want_this"]
more generically:
(JObject)responseString["sub"]["sub_sub"]
I am using Newtonsoft.Json
Thanks!
First off, I would create a class that represents the JSON structure returned from the service call. (http://json2csharp.com/ great utility for auto-generating classes from JSON)
Second, if you are not using Newtonsoft.Json library, I would recommending grabbing that library.
Lastly, use Newtonsoft to deserialize the JSON from the service call into the class you created:
var json = Service.GetJson();
var yourDesiralizedJson = JsonConvert.DeserializeObject<YourJsonToCSharpClass>(json);
var listYouWant = yourDesiralizedJson.IWantChildList.IWantThis;
The below link is appears to be close to the solution as the requester using NewtonSoft.Json as his api to manipulate the object. Appreciate the solutions from other users as well.
look at e.g. here newtonsoft.com/json/help/html/SerializingJSONFragments.htm
The best solution (imo) is to define classes that describe your JSON schema then use DeserializeObject, as suggested by ertdiddy. As a shortcut, you can use DeserializeAnonymousType with incomplete definitions of your schema, taking advantage of JSON's leniency. In your case, this code is working for me:
var testDataFromQuestion = #"
{
""blabla"":1,
""bbb"":false,
""blabla2"":{
""aaa"":25,
""bbb"":25,
""ccc"":0
},
""I_want_child_list_from_this"":{
""total"":15226,
""max_score"":1.0,
""I_want_this"":[
{
""A"":""val1"",
""B"":""val2"",
""C"":""val3""
},
{
""A"":""val1"",
""B"":""val2"",
""C"":""val3""
}
],
""more_blabla"": ""fff""
}";
var anonymousDefinitionOfJson = new {
I_want_child_list_from_this = new {
I_want_this = new Dictionary<string, string>[] {}
}
};
var fullDeserializationOfTestData =
JsonConvert.DeserializeAnonymousType(testDataFromQuestion,
anonymousDefinitionOfJson);
var stuffYouWant = insterestingBits.I_want_child_list_from_this.I_want_this;
Console.WriteLine($"The first thing I want is {stuffYouWant[0]["A"]}");
This outputs the expected value "val1". I'm anonymously defining the minimal classes that get just the data you want, then I'm asking Newtonsoft to parse just enough to populate those classes.

Categories

Resources