Update a JSON file with a model object in C# - c#

I have a json file that looks something like :
[
{
"picklist_typ": "Address Assessment Code",
"picklist_typ_key": null,
"picklist_typ_cd": "DELIVERABLE",
"picklist_typ_dsc": "Address is deliverable : Deliverable",
"ref_order": null,
"dw_trans_ts": "2016-07-17T12:59:15"
},
{
"picklist_typ": "Address Assessment Code",
"picklist_typ_key": null,
"picklist_typ_cd": "NOT-DELIVERABLE",
"picklist_typ_dsc": "Address is not deliverable : Undeliverable",
"ref_order": null,
"dw_trans_ts": "2016-07-17T12:59:15"
},
{
"picklist_typ": "Address Type",
"picklist_typ_key": null,
"picklist_typ_cd": "B",
"picklist_typ_dsc": "Billing Address",
"ref_order": null,
"dw_trans_ts": "2016-07-17T12:59:15"
},
....
Now , I have an object that looks like :
public class GroupMembershipWriteOutput
{
public long? groupKey { get; set; }
public string transOutput { get; set; }
public long? transKey { get; set; }
public string groupCode {get;set;}
public string groupName {get;set;}
}
And it has a value accordingly :
groupKey:121
transOutput: "Success"
transKey:998546
groupCode:"My Group Test"
groupName: "My Created Group Test"
What I want to is ..
I want to read the JSON file and if any entry's picklist_typ_key matches the incoming object's groupKey , I want to update only that object's picklist_typ_cd with groupCode
and picklist_typ_dsc with groupName.
I could read the data from JSON as ...
if(gmwo.transOutput.ToUpper() == "SUCCESS")
{
string json = System.Configuration.ConfigurationManager.AppSettings["PicklistDataPath"];
List<PicklistData> deserializedPicklistData = JsonConvert.DeserializeObject<List<PicklistData>>(System.IO.File.ReadAllText(json));
//find the object that matches
IEnumerable<PicklistData> results = deserializedPicklistData.Where(item => item.picklist_typ_key == gmwo.groupKey.ToString());
if(results != null)
{
**//What is the logic to update only that entry in the json file**
}
Please help me do it .

Could you try the LINQ "All" chain? Something like this:
IEnumerable<PicklistData> results = deserializedPicklistData
.Where(item => item.picklist_typ_key == gmwo.groupKey.ToString())
.All(selectedItem => someFunction(selectedItem));
:
:
someFunction(PicklistData myPick) {
:
}

Related

MongoDB - Cannot create field 'ChildProperty' in element {ParentProperty: is null}

I have a User class:
public class User
{
public Guid? Id { get; set; }
public String? Name { get; set; }
public Address? Address { get; set; }
}
and Address class:
public class Address
{
public String? Street { get; set; }
public String? City { get; set; }
public String? State { get; set; }
}
I am trying to implement partial updating. This is done dynamically, but let's say someone sends to update a User's City and State, I do:
var filter = Builders<BsonDocument>.Filter.Eq("Id", id);
var updates = new List<UpdateDefinition<BsonDocument>>();
updates.Add(Builders<BsonDocument>.Update.Set("Address.City", "New City Value"));
updates.Add(Builders<BsonDocument>.Update.Set("Address.State", "New State Value"));
var update = Builders<BsonDocument>.Update.Combine(updates);
var bsonDocument = await collection.FindOneAndUpdateAsync(filter, update, new FindOneAndUpdateOptions<BsonDocument>
{
ReturnDocument = ReturnDocument.After
});
This works well, except if a User's Address is null. In that case, I get the error:
MongoDB.Driver.MongoCommandException: Command findAndModify failed:
Cannot create field 'City' in element {Address: null}.
Is there any way to ensure the Address object is created so that the City and State properties get set? I would like to do it without getting the current object from the database.
Think that you may work with update with aggregation pipeline to update the Address field dynamically.
Work with $cond operator to check whether Address is null.
If yes, set the whole object to Address.
If no, merge the current Address value with the document.
The query below may looks complex:
db.collection.update({
Id: /* Id */
},
[
{
$set: {
Address: {
$cond: {
if: {
$eq: [
"$Address",
null
]
},
then: {
"City": "New City Value",
"State": "New State Value"
},
else: {
$mergeObjects: [
"$Address",
{
"City": "New City Value",
"State": "New State Value"
}
]
}
}
}
}
}
])
var addressDocument = new BsonDocument
{
{ "City", "New City Value" },
{ "State", "New State Value" }
};
var update = Builders<BsonDocument>.Update.Pipeline(new PipelineStagePipelineDefinition<BsonDocument, BsonDocument>
(
new PipelineStageDefinition<BsonDocument, BsonDocument>[]
{
new BsonDocument("$set",
new BsonDocument("Address",
new BsonDocument("$cond",
new BsonDocument
{
{
"if",
new BsonDocument("$eq",
BsonArray.Create(new object[] { "$Address", null }))
},
{
"then",
addressDocument
},
{
"else",
new BsonDocument("$mergeObjects",
BsonArray.Create(new object[] { "$Address", addressDocument }))
}
}
)
)
)
}
));

How can I deserialize complex json repsonse into easy-to-deserialize structure using Newtonsoft.Json?

Having this strange-looking json response:
{
"invoices":{
"0":{
"invoice":{
"id":"420",
"invoicecontents":{
"0":{
"invoicecontent":{
"name":"Here's the name of the content 0"
}
},
"1":{
"invoicecontent":{
"name":"Here's the name of the content 1"
}
}
}
}
},
"1":{
"invoice":{
"id":"420",
"invoicecontents":{
"0":{
"invoicecontent":{
"name":"Here's the name of the content 0"
}
}
}
}
},
"parameters":{
"limit":"3",
"page":"1",
"total":"420"
}
},
"status":{
"code":"OK"
}
}
How do I change the structure into this easy-to-deserialize one?
{
"invoices":[
{
"id":"420",
"invoicecontents":[
{
"name":"Here's the name of the content 0"
},
{
"name":"Here's the name of the content 1"
}
]
},
{
"id":"420",
"invoicecontents":[
{
"name":"Here's the name of the content 0"
}
]
}
]
}
I'd like to deserialize into List of Invoices as below
class Invoice {
public string Id { get; set; }
[JsonProperty("invoicecontents")]
public InvoiceContent[] Contents { get; set; }
class InvoiceContent {
public string Name { get; set; }
}
}
There's no problem with getting the status code or parameters, I simply do this:
var parsed = JObject.Parse(jsonInvoices);
var statusCode = parsed?["status"]?["code"]?.ToString();
var parameters = parsed?["invoices"]?["parameters"]?;
The real problem starts when I'm trying to achieve easy-to-deserialize json structure I've mentioned before. I've tried something like this:
var testInvoices = parsed?["invoices"]?
.SkipLast(1)
.Select(x => x.First?["invoice"]);
But I can't manage to "repair" invoicecontents/invoicecontent parts.
My goal is to deserialize and store the data.
This isn't JSON.
JavaScript Object Notation literally describes Objects. What you have here is the punchline of a joke that starts out with: "A SQL JOIN, a StringBuilder, and a couple for loops walk into a bar..."
As others have demonstrated, JObject is great for working with JSON that would be impractical to define as classes. You can use Linq to navigate through it, but JSONPath Expressions can be much simpler.
Example 1: Let's get the status code.
var status = parsed.SelectToken(".status.code").Value<string>();
Example 2: Let's 'deserialize' our invoices.
public string Invoice
{
public string Id { get; set; }
public List<string> Content { get; set; }
}
public List<Invoice> GetInvoices(string badJson)
{
var invoices = JObject.Parse(badJson).SelectTokens(".invoices.*.invoice");
var results = new List<Invoice>();
foreach (var invoice in invoices)
{
results.Add(new Invoice()
{
Id = invoice.Value<string>("id"),
Contents = invoice.SelectTokens(".invoicecontents.*.invoicecontent.name")
.Values<string>().ToList()
// Note: JToken.Value<T> & .Values<T>() return nullable types
});
}
return results;
}
try this
var jsonParsed = JObject.Parse(json);
var invoices = ((JObject) jsonParsed["invoices"]).Properties()
.Select(x => (JObject) x.Value["invoice"] ).Where(x => x != null)
.Select(s => new Invoice
{
Id = s.Properties().First(x => x.Name == "id").Value.ToString(),
Contents = ((JObject)s.Properties().First(x => x.Name == "invoicecontents").Value).Properties()
.Select(x => (string) x.Value["invoicecontent"]["name"]).ToList()
}).ToList();
and I simplified your class too, I don't see any sense to keep array of classes with one string, I think it should be just array of strings
public class Invoice
{
public string Id { get; set; }
public List<string> Contents { get; set; }
}
result
[
{
"Id": "420",
"Contents": [
"Here's the name of the content 0",
"Here's the name of the content 1"
]
},
{
"Id": "420",
"Contents": [
"Here's the name of the content 0"
]
}
]

Match "x out of y" fields in a document

I'm writing a service to handle bans for a game and I'm currently a bit stuck trying to write a MongoDB query. Currently I have a collection of "User" objects, and the objects look like this:
public class User
{
public List<Ban> Bans { get; set; }
// some irrelevant additional fields
}
public class Ban
{
public HardwareId HWID { get; set; }
public DateTime Expires { get; set; }
// some irrelevant additional fields
}
public class HardwareId : IEquatable<HardwareId>
{
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get; set; }
public string Field4 { get; set; }
public bool Equals([AllowNull] HardwareId other)
{
if (ReferenceEquals(other, null)) return false;
if (ReferenceEquals(this, other)) return true;
return Field1 == other.Field1 &&
Field2 == other.Field2 &&
Field3 == other.Field3 &&
Field4 == other.Field4;
}
}
What I want to do is have a query that finds all users with a ban where HWID has say 3 out of 4 fields matching. Currently I have a query that only finds users where the HWID match exactly (due to the Equals() implementation), but I'd like to change it. My current code looks like this:
public Ban FindBan(HardwareId hwid)
{
var banBuilder = Builders<Ban>.Filter;
var hwidFilter = banBuilder.Eq(b => b.HWID, hwid);
var expFilter = banBuilder.Gt(b => b.Expires, DateTime.UtcNow);
var banFilter = banBuilder.And(hwidFilter, expFilter);
var user = _users.Find(Builders<User>.Filter.ElemMatch(p => p.Bans, banFilter)).FirstOrDefault();
if (user != null)
{
return user.Bans[0];
}
return null;
}
The only way of solving it I can think of is to write "spaghetti if-statements" in the Equals() function, but I'd like a dynamic solution where I could add multiple "Fields" to the HardwareId class later down the line. Another issue with the FindBan() function currently is the fact that it returns "user.Bans[0]" instead of the actual ban that was found, but I imagine I could solve that by sorting by expiry.
here's one approach to match by minimum of 3 fields of HWID:
var numFieldsToMatch = 3;
var hwid = {
Field1: "one",
Field2: "two",
Field3: "three",
Field4: "four"
};
db.User.find({
$expr: {
$anyElementTrue: {
$map: {
input: "$Bans",
in: {
$gte: [{
$size: {
$filter: {
input: { $objectToArray: "$$this.HWID" },
cond: {
$or: [
{ $and: [{ $eq: ["$$this.k", "Field1"] }, { $eq: ["$$this.v", hwid.Field1] }] },
{ $and: [{ $eq: ["$$this.k", "Field2"] }, { $eq: ["$$this.v", hwid.Field2] }] },
{ $and: [{ $eq: ["$$this.k", "Field3"] }, { $eq: ["$$this.v", hwid.Field3] }] },
{ $and: [{ $eq: ["$$this.k", "Field4"] }, { $eq: ["$$this.v", hwid.Field4] }] }
]
}
}
}
}, numFieldsToMatch]
}
}
}
}
})
https://mongoplayground.net/p/MPwB14o6kuO
it's not possible to translate this mongo query to c# driver code afaik.
see here for an easy way to run this with c#.
You can write a method similar to Equals (or make changes to it), so that you have each condition match in to an array
FldArr[0] = (Field1 == other.Field1);
FldArr[1] = (Field2 == other.Field2);
FldArr[2] = (Field3 == other.Field3);
FldArr[3] = (Field4 == other.Field4);
Then by using LINQ for FldArr, you can find 3/4 true conditions.

How to serialize a list to JSON

I need to serialize the query from my database into a Json format which looks like this:
{
Facilities:[
{
FacilityID:".....",
desc:"....."
},
....
]
}
However, i cant seemed to get the Facilities json array in my codes. How do i edit my codes to show the Facilities? Please Help, Thank you.
My output now:
[
{
FacilityID:".....",
desc:"....."
},
....
]
Not sure how does it works if i added this code:
Public class FacList
{
public List<FacObject> Facilities;
}
Creating of getters and setters
public class FacObject
{
public string facilityID { get; set; }
public string departmentID { get; set; }
public string description { get; set; }
public string block { get; set; }
public string level { get; set; }
public string name { get; set; }
public string openHours { get; set; }
public string closeHours { get; set; }
public string maxBkTime { get; set; }
public string maxBkUnits { get; set; }
public string minBkTime { get; set; }
public string minBkUnits { get; set; }
public FacObject(string facid, string depid, string desc, string b, string l, string n
,string o ,string c, string maxt, string maxu, string mint, string minu)
{
this.facilityID = facid;
this.departmentID = depid;
this.description = desc;
this.block = b;
this.level = l;
this.name = n;
this.openHours = o;
this.closeHours = c;
this.maxBkTime = maxt;
this.maxBkUnits = maxu;
this.minBkTime = mint;
this.minBkUnits = minu;
}
}
Below is my code which queries from the database and serializes the list of facilities into a json string and passes back to the caller! Please help and advice! Help would be appreaciated:)
protected void Page_Load(object sender, EventArgs e)
{
//To get the string to search in facility table
string departmentID = Request.QueryString["DepartmentID"];
string block = Request.QueryString["Block"];
string level = Request.QueryString["Level"];
string name = Request.QueryString["Name"];
//creates a list of fac object
List<FacObject> sqlFacList = new List<FacObject>();
//test if correct
//Only select from a certain department
//select the database for the list of facility that contains
//block, level, name (%)
using (var db = new KioskContext())
{
var facilitys = from f in db.Facilitys
where f.Department.DepartmentID == departmentID
&& (f.Block.Contains(block) && f.Level.Contains(level)
&& f.Name.Contains(name))
orderby f.FacilityID
select new { f.FacilityID, f.DepartmentID, f.Description, f.Block,
f.Level, f.Name, f.OpenHours, f.CloseHours, f.MaxBkTime, f.MaxBkUnits, f.MinBkTime, f.MinBkUnits};
foreach (var fac in facilitys)
{
FacObject facobject = new FacObject(fac.FacilityID, fac.DepartmentID, fac.Description, fac.Block, fac.Level,
fac.Name, fac.OpenHours, fac.CloseHours, fac.MaxBkTime, fac.MaxBkUnits, fac.MinBkTime, fac.MinBkUnits);
sqlFacList.Add(facobject);
}
}
//Serialize into json format output (string)
string json = JsonConvert.SerializeObject(sqlFacList, Formatting.Indented);
//codes to pass back the json string to the iPad
Response.Write(json);
}
Actually you are on the right track. You mentioned in your post you created a class FacList but in your code you are not using it. The example below uses that class produces the exact JSON you're looking for:
Sample code:
var sqlFacList = new FacList();
sqlFacList.Facilities = new List<FacObject>();
sqlFacList.Facilities.Add( new FacObject() { facilityID = "12" });
sqlFacList.Facilities.Add(new FacObject() { facilityID = "34" });
string json = JsonConvert.SerializeObject(sqlFacList, Formatting.Indented);
Output:
{
"Facilities": [
{
"facilityID": "12",
"departmentID": null,
"description": null,
"block": null,
"level": null,
"name": null,
"openHours": null,
"closeHours": null,
"maxBkTime": null,
"maxBkUnits": null,
"minBkTime": null,
"minBkUnits": null
},
{
"facilityID": "34",
"departmentID": null,
"description": null,
"block": null,
"level": null,
"name": null,
"openHours": null,
"closeHours": null,
"maxBkTime": null,
"maxBkUnits": null,
"minBkTime": null,
"minBkUnits": null
}
]
}

MongoDb: documents with variable structure using and C#

I have a HTML form that creates documents with a dynamic structure.
Here below some samples of the data inserted by the users.
A very simple document
{
"name" : "Simple element",
"notes" : "Lorem ipsum rocks",
"values" : [
{
"name" : "An array with 2 values",
"value" : [ 100,200],
"editable" : true
}
]
}
And more complex document
{
"name" : "Complex element",
"notes" : "Lorem ipsum rocks",
"values" : [
{
"name" : "A text value",
"value" : "ABCDEF",
"editable" : true
},
{
"name" : "A numeric value",
"value" : 100,
"editable" : false
},
{
"name" : "A array of 4 values",
"value" : [1,2,3,4],
"editable" : false
},
{
"name" : "A matrix 2x4",
"value" : [[1,2,3,4],[5,6,7,8]],
"editable" : false
}
]
}
The documents must be saved in MongoDB using C# MongoCharp driver and NancyFX.
At the moment the POST is implemented in this way but I'm not sure if this the correct way to handle object with a dynamic structure
Post["/api/docs"] = _ =>
{
//looking for better solution
var json = Request.Body.AsString();
var item = BsonDocument.Parse(json);
database.GetCollection("docs").Insert(item);
return new Response { StatusCode = HttpStatusCode.Created };
};
but can't find a good solution for the GET method
Get["/api/docs"] = _ =>
{
//looking for solution
};
What do you think would be the best solution for this scenario?
There's also another way to solve the problem. Let's call it "the strongly typed solution". I've created two POCO objects
public class DocumentItem
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public String Id { get; set; }
public String Name { get; set; }
public String Notes { get; set; }
public SubItem[] Values { get; set; }
}
public class SubItem
{
public String Name { get; set; }
public Boolean Editable { get; set; }
public Object Value { get; set; }
}
than in the module the read data is implemented as shown below
Get["/api/docs/{id}"] = p => database.GetCollection<DocumentItem>("docs")
.FindOne(Query<DocumentItem>.EQ(x => x.Id, (string)p.id));
Get["/api/docs"] = _ => database.GetCollection<DocumentItem>("docs")
.FindAll()
.ToList();
and I can use binding for the insert in this way
Post["/api/docs"] = _ =>
{
var item = this.Bind<DocumentItem>();
database.GetCollection("docs").Insert(item);
return item;
};
If you are just wanting to return the document from MongoDB as json try something like this
Get["/api/docs/{category}"] = _ =>
{
var filterValue = _.category;
//Search the DB for one record where the category property matches the filterValue
var item = database.GetCollection("docs").FindOne(Query.EQ("category", filterValue))
var json = item.ToJson();
var jsonBytes = Encoding.UTF8.GetBytes(json );
return new Response
{
ContentType = "application/json",
Contents = s => s.Write(jsonBytes, 0, jsonBytes.Length)
};
};

Categories

Resources