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());
}
Related
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];
}
}
}
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.
I have two JSON objects -
json1 = {
"payload": {
"firstName": "John",
"lastName": "Doe",
"code": "test1",
"arrayProp1": [1, 2, 3],
"arrayProp2": [{
"prop1": "value1",
"prop2": "value2"
},
{
"prop1": "2_value1",
"prop2": "2_value2"
}
]
}
}
json2 = {
"payload": {
"code": "newCode",
"arrayProp1": [3,4],
"arrayProp2": [{
"prop1": "newValue1",
"prop2": "newValue2"
}
]
}
}
If I use the built-in merge (json1.Merge(json2)) the result obtained is -
result : {
"payload": {
"firstName": "John",
"lastName": "Doe",
"code": "newCode",
"arrayProp1": [1, 2, 3, 3, 4],
"arrayProp2": [{
"prop1": "value1",
"prop2": "value2"
},
{
"prop1": "newValue1",
"prop2": "newValue2"
},
{
"prop1": "2_value1",
"prop2": "2_value2"
}
]
}
}
Expected result -
{
"payload": {
"firstName": "John",
"lastName": "Doe",
"code": "newCode",
"arrayProp1": [3, 4],
"arrayProp2": [{
"prop1": "newValue1",
"prop2": "newValue2"
}]
}
}
I want to replace the parent property values of json1 based on values provided in json2.
I tried to write a function and this is the current version I have -
string Merge(string req1, string req2) {
try
{
JObject json1 = JObject.Parse(req1);
JObject json2 = JObject.Parse(req2);
foreach (var a in json2.DescendantsAndSelf())
{
if (a is JObject obj)
{
foreach (var prop in obj.Properties())
{
if(json1.SelectTokens(prop.Path).Any())
{
json1[prop.Path] = prop.Value;
}
}
}
}
req1 = json1.ToString();
}
catch(Exception ex)
{
//do nothing
}
return req1; }
There are 2 problems here -
"payload" is identified as property and json1 is replaced fully by json2 because of which I lose some of its properties.
After being replaced, when the loop continues to run, say property 'code' is to be updated, then the property path is payload.code, so on the line json1[prop.path] = prop.Value, instead of updating the existing code in the payload, it creates a new property called payload.code with value "newcode"
The final result of the code above is -
{
"payload": {
"code": "newCode",
"arrayProp1": [3, 4],
"arrayProp2": [{
"prop1": "newValue1",
"prop2": "newValue2"
}],
"payload.code": "newCode",
"payload.arrayProp1": [3, 4],
"payload.arrayProp2": [{
"prop1": "newValue1",
"prop2": "newValue2"
}],
"payload.arrayProp1[0].prop1": "newValue1",
"payload.arrayProp1[0].prop2": "newValue2"
}
}
Can someone please help me with this?
Your requirement is that array contents are replaced rather than concatenated when merging two JSON objects with JContainer.Merge(). You can achieve this via the JsonMergeSettings.MergeArrayHandling setting, which has the following values:
Concat 0 Concatenate arrays.
Union 1 Union arrays, skipping items that already exist.
Replace 2 Replace all array items.
Merge 3 Merge array items together, matched by index.
Specifically MergeArrayHandling.Replace will work as required:
json1.Merge(json2, new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Replace
});
Demo fiddle here.
Please let me know the C# equivalent of below MongoDB query. It works fine in Robo3T for MongoDB
db.UserProfile.aggregate([
{
$project: {
SName:1,
FName:1,
DemRole: {
$filter: {
input: "$DemRole",
as: "item",
cond: { $eq: [ "$$item.Name", "FO" ] }
}
}
}
}
])
I could achieve this by below C# code
var stage1 = "{ $project: { SName: 1, FName: 1, DemRole: { $filter: { input: '$DemRole', as: 'item', cond: { $eq: ['$$item._id', '5bc49241f12541c3aaa098ee' ] } } } } }";
var query = collection.Aggregate(new AggregateOptions() { AllowDiskUse = true })
.AppendStage<BsonDocument>(stage1).ToEnumerable();
return query?.Select(point => BsonSerializer.Deserialize<RetUserProfile4Role>(point)).ToList();
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 );