JSON.net Array diff in C# - c#

I have two arrays
"commanditaires_data": [
{
"id": "254",
"level": 78
},
{
"id": "255",
"level": 22
}
]
"commanditaires_data": [
{
"id": "254",
"level": 78
}, {
"id": "255",
"level": 22
},
{
"id": "255",
"level": 22
}
]
I don't manage to get the missing items in the first array.
I need to do a differential between these two arrays.
I tried to use Except() but I send me every items of the first arrays
I'm using JSON.NET so my vars for arrays are :
var srcObjs = source.Children().ToList().OfType<JObject>();
var tarObjs = target.Children().ToList().OfType<JObject>();
var diff = tarObjs.Except(src.objs);
=> It sends me all items in tarObjs.
I need to handle many cases :
If srcObjs.Count() < tarObjs.Count() //Added item(s)
If srcObjs.Count() > tarObjs.Count() // Deleted item(s)
Else //Edited Item(s)
I also tried to use linq but without success

public class CommanditairesEqualityComparer: IEqualityComparer<Commanditaires>
{
public bool Equals(Commanditaires first, Commanditaires second)
{
if (first== null && first== null)
return true;
return first.Id == second.Id
&& first.Level == second.Level;
}
public int GetHashCode(Commanditaires model)
{
return model.Id.GetHashCode() + model.Level.GetHashCode();
}
}
and then
var comparer = new CommanditairesEqualityComparer();
var distinctItems = firstList.Except(secondList, comparer );

Related

How to convert list of fields into json object [duplicate]

This question already has an answer here:
Convert dot notation to JSON
(1 answer)
Closed last year.
I have list of fields as a string:
[
"Container.Description",
"Container.Groups.12.Users.11.Name",
"Container.Groups.12.Users.12.Name",
"Container.Groups.13.Users.21.Name",
"Container.Groups.13.Users.22.Name"
]
Is there a way to convert this array of fields to json object like:
{
"Container": {
"Description": "",
"Groups": [
{
"12": {
"Users": [
{ "11": { "Name": "" }},
{ "12": { "Name": "" }}
]
}
},
{
"13": {
"Users": [
{ "21": { "Name": "" }},
{ "22": { "Name": "" }}
]
}
}
]
}
}
I need this code into typescript or c#.
Thanks in advance.
It maybe not a very good solution, but it indeed works...
handleData(data: string[]) {
const res = {};
data.forEach(dataItem => this.changeObjectByStr(res, dataItem));
return res;
}
private changeObjectByStr(currentObject: { [key: string]: any }, str: string) {
const keyList = str.split('.');
let usedObject = currentObject;
for (let i = 0; i < keyList.length; i++) {
const key = keyList[i];
// The key existed
if (usedObject[key]) {
usedObject = usedObject[key];
continue;
}
// The key not exist:
// This is the last key
if (i === keyList.length - 1) {
usedObject[key] = '';
} else {
// This is not the last key
usedObject[key] = {};
usedObject = usedObject[key];
}
}
}

One to many relationship expressed in JSON

I am having a small problem in some LINQ I am writing
public async Task<BookingEntry[]> AuditInvoiceForAPI(BookingSearchFilter filter)
{
if (filter == null)
throw new ArgumentNullException();
using var speedPayDbContext = this.speedPayDbContextFactory();
var results = await speedPayDbContext.spAuditInvoiceForAPI(filter.BookingID, filter.VendorID, filter.InvoiceID).ToArrayAsync();
var BookingResponse = results
.Select(r =>
{
return new BookingEntry()
{
Result = r.Result,
BookingID = r.BookingID,
Balance = r.Balance,
TotalCost = r.TotalCost,
CostDetails =
results.Where(cd => cd.BookingID == r.BookingID)
.Select(a => new Costdetail(a.Amount, a.ChargeCodeID)).ToArray(),
Message = r.Message,
};
})
.ToArray();
return BookingResponse;
here is the result set
BookingId
Result
Message
TotalCost
BillKey
Balance
ChargeCodeId
amount
8446670
0
Success
498.54
8446670
89.8000
11
75.00
8446670
0
Success
498.54
8446670
89.8000
14
14.80
I am getting dup in the JSON which I can understand why. There are indeed two rows returned one booking entry with two costdetails.
Can someone please help me with get rid of the dup?
Its probably as simple as I don't understand SelectMany.
This is in essence a simple join first 6 columns are the header and the last two are the detail records and you can see that the bookingID is what they are joined on.
Thanks very much
{
"bookingID": "8446670",
"result": "0",
"message": "Success",
"totalCost": 498.54,
"balance": 89.8,
"costDetails": [
{
"chargeCodeID": 11,
"amount": 75
},
{
"chargeCodeID": 14,
"amount": 14.8
}
]
},
{
"bookingID": "8446670",
"result": "0",
"message": "Success",
"totalCost": 498.54,
"balance": 89.8,
"costDetails": [
{
"chargeCodeID": 11,
"amount": 75
},
{
"chargeCodeID": 14,
"amount": 14.8
}
]
}
]

MongoDb conditional UpdateMany statement including expression using .net driver

I have the following document:
{
"_id": {
"$oid": "5fc232d9b9b390623ce498ee"
},
"trigger": {
"type": 0,
"interval": 15
},
"startTime": {
"$numberLong": "1608078905"
},
"state": 0,
"assigned_to": ""
}
And I have a UpdateMany statement that only updates documents that their startTime (timespan) + interval (seconds) is lower than equal to the current timestamp with the value of 'computer a', (UPDATE) and set the 'state' attribute to '1':
(UPDATE)
db.mycollection.updateMany(
{ state: 0 },
[
{ $set: { assigned_to: { $switch: {
branches: [
{ case: { $lte: [ { $add: [ "$startTime", "$trigger.interval" ] }, new Timestamp() ] }, then: "computer a" }
],
default: ""
} } } },
{ $set: { state: 1 } }
]
)
My goals are:
(RESOLVED) To update the state to '1' in addition to the 'assigned_to' field
(RESOLVED) To automatically calculate the timestamp (the current time)
Eventually, to have it working with my C# .NET Core application (if possible - with the driver syntax so it will be strongly-typed)
On the C# side I've tried the following (with no success):
var results = _jobs.UpdateMany(
f => (f.StartTime + f.Trigger.Interval) <= DateTimeOffset.Now.ToUnixTimeSeconds(),
o => o.AssignedTo == "computer a");
Another approach I tried:
var builder = Builders<Job>.Filter;
var filters =
builder.Lte(o => o.StartTime + o.Trigger.Interval, DateTimeOffset.Now.ToUnixTimeSeconds()) &
builder.Eq(o => o.AssignedTo, string.Empty);
var updateDefinition =
Builders<Job>.Update.Set(x => x.AssignedTo, Environment.MachineName);
var result = _jobs.UpdateMany(filters, updateDefinition);
After spending hours, I only achieve the UpdateMany statement above working.
If you have ideas about how it could be done in a better way - I'm free for offers.

How can I find Index of object in JArray based on a matching search?

I am trying to find the index of an object inside an JArray based on a search.
What I want to end up is the ability to do this
JArray articles = (JArray)DetailedData["product"]["articlesList"][x]
The problem is I dont know how to find what x is
The JSON looks like this ( severly cut down )
"articlesList": [
{
"code": "0587026001",
I want to find the index x where code = 0587026001 but I cannot figure it out
I tried
var index = articlesList.IndexOf("$.[?(#.<code>=='0587026001')]");
but that returned -1
How can I find the index x where code matches as per above?
Hope I understood your question correctly. You can find the Index as following
var result = JObject.Parse(json);
var selectedIndex = result["articlesList"].Select((x,index)=> new { Code= x.Value<string>("code"), Node = x, Index = index})
.Single(x=>x.Code == "0587026003")
.Index;
You can use the SelectTokens to find the relevant tokens based on filter criteria.
The below code selects all the token under articleList which has code equals to '0587026001'
IEnumerable<JToken> tokens = o.SelectTokens("$..articlesList[?(#.Code == '0587026001')]");
Check this dotnet fiddle - https://dotnetfiddle.net/EJvyfp which demonstrates the usage of it.
Sample code:
public class Program
{
public static void Main()
{
JArray o = JArray.Parse(#"[{
'Stores': [
'Lambton Quay',
'Willis Street'
],
'articlesList': [
{
'Code': '0587026001',
'Name': 'Acme Co',
'Products': [
{
'Name': 'Anvil',
'Price': 50
}
]
},
{
'Code': '456',
'Name': 'Contoso',
'Products': [
{
'Name': 'Elbow Grease',
'Price': 99.95
},
{
'Name': 'Headlight Fluid',
'Price': 4
}
]
}
]
},
{
'Stores': [
'ABD',
'XYZ'
],
'articlesList': [
{
'Code': '789',
'Name': 'Acme Co',
'Products': [
{
'Name': 'Anvil',
'Price': 50
}
]
},
{
'Code': '1234',
'Name': 'Contoso',
'Products': [
{
'Name': 'Elbow Grease',
'Price': 99.95
},
{
'Name': 'Headlight Fluid',
'Price': 4
}
]
}
]
}]");
//Console.WriteLine(o);
IEnumerable<JToken> tokens = o.SelectTokens("$..articlesList[?(#.Code == '0587026001')]");
Console.WriteLine(tokens.Count());
Console.WriteLine(tokens.First());
Console.WriteLine("Hello World");
}
}
The above program returns the output as
{
"Code": "0587026001",
"Name": "Acme Co",
"Products": [
{
"Name": "Anvil",
"Price": 50
}
]
}
Here's how you could do it:
private static int GetIndex(string code)
{
string json = "{\"product\": {\"articleList\": [{\"code\": \"0587026001\"}, {\"code\": \"0587026002\"}]}}";
var jObject = JObject.Parse(json);
var articleList = JArray.Parse(jObject["product"]["articleList"].ToString());
var requiredArticle = articleList.First(a => a["code"].ToString().Equals(code));
return articleList.IndexOf(requiredArticle);
}

How to deserialize multiple json in C#?

I'm loading some information to my Xamarin application through multiple json strings. When I run the application, it gives me this error:
Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[APIPost]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List
that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'postid', line 1, position 9.'
Code to get the json:
HttpClient client = new HttpClient();
var result = await client.GetStringAsync($"{APIConfig.Uri}/post/getPost/{APIConfig.Token}/{User.ID}/{User.Token}");
List<APIPost> response = JsonConvert.DeserializeObject<List<APIPost>>(result);
foreach (APIPost post in response)
{ //Code }
Class APIPost:
class APIPost
{
public string state { get; set; }
public string postid { get; set; }
public string userid { get; set; }
public string image { get; set; }
public string captation { get; set; }
public string location { get; set; }
public string date { get; set; }
public string likes { get; set; }
}
This is the json I get:
{
"postid": "2",
"userid": "2",
"image": "asdasdasd",
"captation": "asdasd",
"location": null,
"date": "2019-07-29 20:24:28",
"likes": "4"
}{
"postid": "1",
"userid": "2",
"image": "susfdfjsadv",
"captation": "This is just a test.",
"location": null,
"date": "2019-07-29 19:58:04",
"likes": "2"
}
The problem isn't with C# or with the JSON serialization library you're using. The JSON you're receiving from the server is invalid. As such, no standard JSON parsing library can be expected to successfully parse it. You'll either need to write your own, or correct the JSON to be valid.
A valid object would look like:
{
"postid": "2",
"userid": "2",
"image": "asdasdasd",
"captation": "asdasd",
"location": null,
"date": "2019-07-29 20:24:28",
"likes": "4"
}
And a valid array of objects would look like:
[
{
"postid": "2",
"userid": "2",
"image": "asdasdasd",
"captation": "asdasd",
"location": null,
"date": "2019-07-29 20:24:28",
"likes": "4"
},
{
"postid": "1",
"userid": "2",
"image": "susfdfjsadv",
"captation": "This is just a test.",
"location": null,
"date": "2019-07-29 19:58:04",
"likes": "2"
}
]
There's no such thing as "multiple JSON" within the same structure. You either have a valid structure or you don't. You can certainly have multiple structures, but you can't have them all mashed together into one like that.
In short... fix the server-side code to send a valid response.
Have you any control over the source of that json?
You need to provide something like:
[
{...},
{...}
]
And you will be fine
As #lasse-v-karlsen says in a comment, the server should be fixed but there's a way to parse this with Newtonsoft.Json (in case we're in one of those JSON-streaming scenarios):
using (var file = new StreamReader(#"D:\Temp\test.json", Encoding.UTF8))
using (var reader = new Newtonsoft.Json.JsonTextReader(file))
{
reader.SupportMultipleContent = true;
var serializer = JsonSerializer.CreateDefault();
serializer.Deserialize(reader).Dump();
while (reader.Read())
serializer.Deserialize(reader).Dump();
}
In case you're not fond on adding the dependency to json.net, then I've cooked a very simple JSON splitter:
IEnumerable<string> SplitJsonStream(string jsonString)
{
IEnumerable<string> splitJsonStreamInner(char? head, string tail, uint braceNestingLevel, bool insideSubString, string acc)
{
if (!head.HasValue)
{
if (braceNestingLevel != 0)
throw new ArgumentException("jsonString seems to be invaid");
yield break;
}
char? newHead = null;
string newAcc = null;
if (!String.IsNullOrEmpty(tail))
{
newHead = tail.First();
}
if (head.HasValue)
{
newAcc = acc + head.Value;
}
var newTail = newHead == null ? null : tail.Substring(1);
if (!insideSubString && head == '"') {
foreach(var subAcc in splitJsonStreamInner(newHead, newTail, braceNestingLevel, true, newAcc))
yield return subAcc;
yield break;
}
if (insideSubString)
{
if (head == '"')
{
foreach (var subAcc in splitJsonStreamInner(newHead, newTail, braceNestingLevel, false, newAcc))
yield return subAcc;
yield break;
}
foreach (var subAcc in splitJsonStreamInner(newHead, newTail, braceNestingLevel, insideSubString, newAcc))
yield return subAcc;
yield break;
}
if (head == '{')
{
foreach (var subAcc in splitJsonStreamInner(newHead, newTail, braceNestingLevel + 1, insideSubString, newAcc))
yield return subAcc;
yield break;
}
else if (head == '}')
{
if (braceNestingLevel == 0)
throw new ArgumentException("jsonString seems to be invalid");
var newNestingLevel = braceNestingLevel - 1;
if (newNestingLevel == 0) {
yield return newAcc;
newAcc = String.Empty;
}
foreach (var subAcc in splitJsonStreamInner(newHead, newTail, newNestingLevel, insideSubString, newAcc))
yield return subAcc;
yield break;
}
else
{
foreach (var subAcc in splitJsonStreamInner(newHead, newTail, braceNestingLevel, insideSubString, newAcc))
yield return subAcc;
yield break;
}
}
jsonString = jsonString.Trim();
if (jsonString == string.Empty)
yield break;
if (!jsonString.StartsWith("{"))
throw new ArgumentException("jsonString should start with {");
if (!jsonString.EndsWith("}"))
throw new ArgumentException("jsonString should end with }");
foreach (var sub in splitJsonStreamInner(jsonString.First(), jsonString.Substring(1), 0, false, String.Empty))
yield return sub;
}
The proof that the above works is this unit test:
[Fact()]
public void JsonSplitterTests()
{
Assert.Empty(SplitJsonStream(String.Empty));
Assert.Throws<ArgumentException>(() => SplitJsonStream("x").Count()); // not startswith {
Assert.Throws<ArgumentException>(() => SplitJsonStream("{x").Count()); // not endswith }
var fooJson = "{ \"foo\": 1 }";
var splitted = SplitJsonStream(fooJson);
Assert.Equal(1, splitted.Count());
Assert.Equal(fooJson, splitted.ElementAt(0));
var barJson = "{ \"bar\": 2 }";
splitted = SplitJsonStream(fooJson + barJson);
Assert.Equal(2, splitted.Count());
Assert.Equal(fooJson, splitted.ElementAt(0));
Assert.Equal(barJson, splitted.ElementAt(1));
var bazJson = "{ \"baz\": 3 }";
splitted = SplitJsonStream(fooJson + barJson + bazJson);
Assert.Equal(3, splitted.Count());
Assert.Equal(fooJson, splitted.ElementAt(0));
Assert.Equal(barJson, splitted.ElementAt(1));
Assert.Equal(bazJson, splitted.ElementAt(2));
// ends with bad nesting level
Assert.Throws<ArgumentException>(() => SplitJsonStream("{{x}").Count());
Assert.Throws<ArgumentException>(() => SplitJsonStream("{x}}").Count());
// edge case
Assert.Equal(1, SplitJsonStream("{ \"foo\": \"{\" }").Count());
//whitespace in front
Assert.Equal(2, SplitJsonStream("\n{ \"foo\": 1 }{ \"bar\": 2 }").Count());
Assert.Equal(2, SplitJsonStream("\r\n{ \"foo\": 1 }{ \"bar\": 2 }").Count());
Assert.Equal(2, SplitJsonStream("\t{ \"foo\": 1 }{ \"bar\": 2 }").Count());
Assert.Equal(2, SplitJsonStream(" { \"foo\": 1 }{ \"bar\": 2 }").Count());
//whitespace at the end
Assert.Equal(2, SplitJsonStream("{ \"foo\": 1 }{ \"bar\": 2 }\n").Count());
Assert.Equal(2, SplitJsonStream("{ \"foo\": 1 }{ \"bar\": 2 }\r\n").Count());
Assert.Equal(2, SplitJsonStream("{ \"foo\": 1 }{ \"bar\": 2 }\t").Count());
Assert.Equal(2, SplitJsonStream("{ \"foo\": 1 }{ \"bar\": 2 } ").Count());
//whitespace in the middle
Assert.Equal(2, SplitJsonStream("{ \"foo\": 1 }\n{ \"bar\": 2 }").Count());
Assert.Equal(2, SplitJsonStream("{ \"foo\": 1 }\r\n{ \"bar\": 2 }").Count());
Assert.Equal(2, SplitJsonStream("{ \"foo\": 1 }\t{ \"bar\": 2 }").Count());
Assert.Equal(2, SplitJsonStream("{ \"foo\": 1 } { \"bar\": 2 }").Count());
}

Categories

Resources