I have an application that serializes a list of objects to json, then sends this over a socket connection to a client.
On the client side, I'm using JsonConvert.PopulateObject() to populate an existing list of objects with the newly received json data. However, the objects are continually being appended to the list instead of reusing the existing objects in the list if there are any duplicates.
Here is the class I'm serializing/deserializing:
public class Process : INotifyPropertyChanged
{
private int _id;
private string _name;
public int ID
{
get { return _id; }
set
{
if (value != _id)
{
_id = value;
NotifyPropertyChanged("ID");
}
}
}
public string Name
{
get { return _name; }
set
{
if (value != _name)
{
_name = value;
NotifyPropertyChanged("Name");
}
}
}
public Process() { }
public Process(int id, string name)
{
ID = id;
Name = name;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Here is the PopulateObject code I'm using:
JsonSerializerSettings settings = new JsonSerializerSettings() { PreserveReferencesHandling = PreserveReferencesHandling.Objects, ObjectCreationHandling = ObjectCreationHandling.Auto };
ObservableCollection<Process> Processes = new ObservableCollection<Process>();
JsonConvert.PopulateObject(response, Processes, settings);
It seems like json.net just doesn't know an object is a duplicate reference despite the property values being exactly the same. I've tried multiple combinations of the JsonObject attribute on my class (IsReference = true, Id = "ID"), etc. I cannot seem to get json to recognize two objects are the same if their ID property is matching.
Thats not what PreserveReferencesHandling does. It is used to avoid serializing duplicate objects, effectively shortening the json result. Example:
List<NameValuePair> nvpList = new List<NameValuePair>();
NameValuePair z = new NameValuePair(Name="Ziggy", Value= 42);
nvpList.Add(z);
nvpList.Add(new NameValuePair("Zoey", 3));
nvpList.Add(z);
JsonSerializerSettings settings = new JsonSerializerSettings() {
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
};
string json = JsonConvert.SerializeObject(nvpList, Formatting.Indented,
settings);
My List has 2 Ziggy objects, but the resulting json is:
[
{
"$id": "1",
"Name": "Ziggy",
"Value": 42
},
{
"$id": "2",
"Name": "Zoey",
"Value": 3
},
{
"$ref": "1"
}
]
It assigns an internal $Id and then references that for the duplicate object. This only applies where the object references are the same. If 2 Ziggy objects had been created and added - even with the same values - the json would reflect that and duplicate them.
To prevent duplicates in the list when deserializing, you will have to use a mapper or the equivalent code:
List<NameValuePair> tmp = JsonConvert.DeserializeObject<List<NameValuePair>>(json);
foreach (NameValuePair nvp in tmp)
{
if (nvpList.Where(w => w.Value == nvp.Value).FirstOrDefault() == null)
{
nvpList.Add(nvp);
}
}
This adds just those where the Value/Id does not already exist in the master list, add whatever logic you need to detect dupes.
See: Preserving Object References
Have your tried overriding the Equals() function of the serilized object
public override bool Equals(Process p)
{
// If parameter is null return false:
if ((object)p == null)
{
return false;
}
// Return true if the fields match:
return (ID == p.ID);
}
Related
I'm managing a data structure that looks like the following
[
{
"name": "Yada yada",
"dataModels": [
{
"entity": "Table",
"columns": [
[
{
"column": "ColumnA",
"value": " \"StringA\""
},
{
"column": "ColumnB",
"value": " \"StringB\""
}
],
...,
[
{
"column": "ColumnA",
"value": " \"StringA\""
},
{
"column": "ColumnB",
"value": " \"StringB\""
}
],
...
]
}
]
}
]
Every object in the columns list is a HashSet of ColumnValues, a class I created and is defined the following way:
public class ColumnValues
{
private string column;
private string value;
public ColumnValues(string col, string val)
{
column = col;
value = val;
}
public string Column
{
get {return column;}
}
public string Value
{
get {return value;}
}
public override bool Equals(object obj)
{
return obj is ColumnValues values &&
column == values.column &&
value == values.value &&
Column == values.Column &&
Value == values.Value;
}
public override int GetHashCode()
{
return HashCode.Combine(column, value, Column, Value);
}
}
So far, so good. However the issue is that in columns, I'm having duplicates of lists. columns is a field in DataModel, another class:
public class DataModel
{
private string entity;
private List<HashSet<ColumnValues>> columns;
private string rule;
public DataModel(string entityName, List<HashSet<ColumnValues>> columnList)
{
entity = entityName;
columns = columnList;
}
public string Entity
{
get { return entity; }
}
public List<HashSet<ColumnValues>> Columns
{
get { return columns; }
set { columns = value; }
}
public string Rule
{
set { rule = value; }
}
}
I'm not understanding why the Set is allowing the existence of duplicates.
I do add that I'm returning the columns list after applying Distinct().ToList():
var dataModels = new List<DataModel>();
GatherColumns(code.Syntax);
dataModels.ForEach(dataModel => dataModel.Columns = dataModel.Columns.Distinct().ToList());
return dataModels;
TIA!
EDIT:
As per Tim's comment, I've provided the override methods Equals and GetHashCode.
I've also decided to define Columns in DataModel as a List of HashSets.
However, unfortunately the result hasn't changed.
EDIT N°2:
I've decided to create a class implementing the IEqualityComparer interface. However, little effect so far:
public class ColumnValuesComparer : IEqualityComparer<HashSet<ColumnValues>>
{
public bool Equals(HashSet<ColumnValues> c1, HashSet<ColumnValues> c2)
{
return c1.Equals(c2);
}
public int GetHashCode(HashSet<ColumnValues> obj)
{
return obj.GetHashCode();
}
}
Used here:
var dataModels = new List<DataModel>();
GatherColumns(code.Syntax);
ColumnValuesComparer comparer = new ColumnValuesComparer();
dataModels.ForEach(dataModel => dataModel.Columns = dataModel.Columns.Distinct(comparer).ToList());
return dataModels;
At this point I'm pretty much stuck, and don't know where to go moving forward.
After a bit of searching a documentation, I fell upon the concept of SetComparer.
They allow me to create objects that evaluate deep nested equality within HashSets.
So, when I try to create my HashSet of HashSets, I must pass that method:
var set = new HashSet<HashSet<ColumnValues>>(HashSet<ColumnValues>.CreateSetComparer());
set.Add(columnValues);
I'd like to be able to control how JSON .NET generates its meta reference IDs such as "$id": "1". Take the following code:
public class Person
{
public string Name { get; set; }
public Person Mother { get; set; }
}
.
var settings = new Newtonsoft.Json.JsonSerializerSettings
{
PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects,
Formatting = Newtonsoft.Json.Formatting.Indented
};
Newtonsoft.Json.JsonConvert.DefaultSettings = () => settings;
var person = new Person
{
Name = "bob",
Mother = new Person { Name = "jane" }
};
var personJson = JsonConvert.SerializeObject(person);
var motherJson = JsonConvert.SerializeObject(person.Mother);
The JSON for person looks like this:
{
"$id": "1",
"Name": "bob",
"Mother": {
"$id": "2",
"Name": "jane",
"Mother": null
}
}
However, if I serialize person.Mother directly, the JSON looks like this:
{
"$id": "1",
"Name": "jane",
"Mother": null
}
In the first JSON, Jane is "$id": "2", but serializing Jane directly is "$id": "1". This is the behavior I'd expect under normal conditions as the serializer assigns the IDs in the order it traverses the objects, but I'd really like to override the ID generation so that I could make it a hash of the object reference itself. This way, Jane would generate the same ID per running instance of the program every time regardless if serialized as a member of a parent or serialized individually.
UPDATE
Per sample code in selected answer and recommendation in comment, I have used IReferenceResolver. It turns out that I can't use it, though, but I'll include the code below anyway. The reason why this won't work is because I am trying to bastardize JSON.NET as a quick and dirty cloning tool, so I can't fault it for not suiting my needs. I've since fallen back on my own custom cloning utility, so I no longer need this.
public class ObjectReferenceResolver : Newtonsoft.Json.Serialization.IReferenceResolver
{
readonly Dictionary<object, int> objectDic = new Dictionary<object, int>();
int maxId = 0;
//Called during serialization
public string GetReference(object context, object value)
{
//This method will return the meta $id that you choose. In this example, I am storing
//object references in a dictionary with an incremented ID. If the reference exists, I
//return its ID. Otherwise, I increment the ID and add the reference to the dictionary.
var id = 0;
if (objectDic.ContainsKey(value))
{
id = objectDic[value];
}
else
{
objectDic[value] = maxId++;
}
return id.ToString();
}
//Called during serialization
public bool IsReferenced(object context, object value)
{
//Return whether or not value exists in a reference bank.
//If false, the JSON will return as a full JSON object with "$id": "x"
//If true, the JSON will return "$ref": "x"
return objectDic.ContainsKey(value);
}
//Called during deserialization
public void AddReference(object context, string reference, object value)
{
//This method is called after the deserializer has created a new instance of the
//object. At this time, it's only the initial instance and no properties have been set.
//This method presents a problem because it does not allow you to create the instance or
//retrieve it from a repo and then return it for later use by the reference resolver.
//Therefore, I have to find the existing object by $id, remove it, and then add the new
//object created by the deseralizer. This creates the possibility for two instances of
//the same data object to exist within the running application, so, unfortunately, this
//will not work.
var e = objectDic.First(x => x.Value.ToString() == reference).Key;
objectDic.Remove(e);
objectDic[value] = reference.ParseInt().Value;
}
//Called during deserialization
public object ResolveReference(object context, string reference)
{
//This method retrieves an existing reference by $id and returns it.
var value = objectDic.FirstOrDefault(x => x.Value.ToString() == reference).Key;
return value;
}
}
As per others have recommended, you need a custom IReferenceResolver:
class PersonNameAsIdResolver : IReferenceResolver
{
public void AddReference(object context, string reference, object value)
{
// This method is called during deserialize for $id
}
public string GetReference(object context, object value)
{
// Returns person name as value of $id
return ((Person)value).Name;
}
public bool IsReferenced(object context, object value)
{
// Returns false, so that $id is used, not $ref.
return false;
}
public object ResolveReference(object context, string reference)
{
// This method is called during deserialize for $ref
return null;
}
}
To use that:
var settings = new Newtonsoft.Json.JsonSerializerSettings
{
PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects,
Formatting = Newtonsoft.Json.Formatting.Indented
};
settings.ReferenceResolverProvider = ()=> new PersonNameAsIdResolver();
UPDATE
Answer to the OP's update
AddReference is called while an object is being populated, so it has been too late to replace the object. To be able to find and populate desired object, you need a JsonConverter, which is called before the reference resolver:
class PersonJsonConverter : JsonConverter
{
private readonly PersonNameAsIdResolver _idResolver;
public PersonJsonConverter(PersonNameAsIdResolver idResolver)
{
_idResolver = idResolver;
}
public override bool CanConvert(Type objectType)
=> objectType == typeof(Person);
// Can't write. There's nothing to changing for writing scenario.
public override bool CanWrite => false;
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
var token = JToken.ReadFrom(reader);
if (token.Type == JTokenType.Null)
{
return null;
}
var obj = (JObject)token;
// The code below calls the resolver to find the existing instance.
// This can stop JSON.NET creating a new instance.
Person instance = null;
var #id = obj["$id"].Value<string>();
if (#id != null)
{
instance = (Person)_idResolver.ResolveReference(this, #id);
}
else
{
var #ref = obj["$ref"]?.Value<string>();
if (#ref != null)
{
instance = (Person)_idResolver.ResolveReference(this, #ref);
}
}
// Assuming can't resolve, create a new instance.
if (instance == null)
{
instance = new Person();
}
// This will populate existing Person object if found
serializer.Populate(obj.CreateReader(), instance);
return instance;
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
throw new NotSupportedException();
}
}
And the default serialization settings should look like:
var settings = new Newtonsoft.Json.JsonSerializerSettings
{
PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects,
Formatting = Newtonsoft.Json.Formatting.Indented
};
var idResolver = new PersonNameAsIdResolver();
settings.Converters.Add(new PersonJsonConverter(idResolver));
settings.ReferenceResolverProvider = () => idResolver;
A possible solution would be the following:
Replace PreserveReferncesHandling from Objects to None:
PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.None
Add an Id property in Person class:
public class Person
{
public string Id { get; set; }
public string Name { get; set; }
public Person Mother { get; set; }
}
The complete solution is as follows:
using System;
using Newtonsoft.Json;
namespace ControlJsonId
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("INICIO");
var settings = new Newtonsoft.Json.JsonSerializerSettings
{
PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.None,
Formatting = Newtonsoft.Json.Formatting.Indented,
};
Newtonsoft.Json.JsonConvert.DefaultSettings = () => settings;
var person = new Person
{
Id = Guid.NewGuid().ToString(),
Name = "bob",
Mother = new Person { Id = string.Empty, Name = "jane" }
};
var personJson = JsonConvert.SerializeObject(person);
Console.WriteLine(personJson);
var motherJson = JsonConvert.SerializeObject(person.Mother);
Console.WriteLine(motherJson);
Console.WriteLine("FIN");
Console.ReadKey();
}
}
public class Person
{
public string Id { get; set; }
public string Name { get; set; }
public Person Mother { get; set; }
}
}
The result is:
INICIO
{
"Id": "76d6b5f0-2be8-4d1d-aafe-fe1b4b7d6ae1",
"Name": "bob",
"Mother": {
"Id": "",
"Name": "jane",
"Mother": null
}
}
{
"Id": "",
"Name": "jane",
"Mother": null
}
FIN
I'm working in an ASP.NET webapi codebase where we rely heavily on the automatic support for JSON deserialization of message bodies into .NET objects via JSON.NET.
As part of building out patch support for one of our resources, I'd very much like to distinguish between an optional property in the JSON object that's not present, vs. that same property that's explicitly to null. My intention is to use the first for "don't change what's there" vs. "delete this thing."
Does anyone know if it's possible to mark up my C# DTOs so that when they're deserialized that JSON.NET can tell me which case it was? Right now they're just come up as null, and I can't tell why.
Conversely, if anyone can come up with a better design that doesn't require me to do it this way while still supporting the patch verb, I'd love to hear your proposal.
As a concrete example, consider this payload that would be passed to put:
{
"field1": "my field 1",
"nested": {
"nested1": "something",
"nested2": "else"
}
}
Now, if I just wanted to update field1, I should be able to send this as an HTTP patch:
{
"field1": "new field1 value"
}
and the nested values would remain untouched. However, if I sent this:
{
"nested": null
}
I want to know this means I should explicitly remove the nested data.
If you use Json.Net's LINQ-to-JSON API (JTokens, JObjects, etc.) to parse the JSON, you can tell the difference between a null value and a field that simply doesn't exist in the JSON. For example:
JToken root = JToken.Parse(json);
JToken nested = root["nested"];
if (nested != null)
{
if (nested.Type == JTokenType.Null)
{
Console.WriteLine("nested is set to null");
}
else
{
Console.WriteLine("nested has a value: " + nested.ToString());
}
}
else
{
Console.WriteLine("nested does not exist");
}
Fiddle: https://dotnetfiddle.net/VJO7ay
UPDATE
If you're deserializing into concrete objects using Web API, you can still use the above concept by creating a custom JsonConverter to handle your DTOs. The catch is that there needs to be a place on your DTOs to store the field status during deserialization. I would suggest using a dictionary-based scheme like this:
enum FieldDeserializationStatus { WasNotPresent, WasSetToNull, HasValue }
interface IHasFieldStatus
{
Dictionary<string, FieldDeserializationStatus> FieldStatus { get; set; }
}
class FooDTO : IHasFieldStatus
{
public string Field1 { get; set; }
public BarDTO Nested { get; set; }
public Dictionary<string, FieldDeserializationStatus> FieldStatus { get; set; }
}
class BarDTO : IHasFieldStatus
{
public int Num { get; set; }
public string Str { get; set; }
public bool Bool { get; set; }
public decimal Dec { get; set; }
public Dictionary<string, FieldDeserializationStatus> FieldStatus { get; set; }
}
The custom converter would then use above LINQ-to-JSON technique to read the JSON for the object being deserialized. For each field in the target object, it would add an item to that object's FieldStatus dictionary indicating whether the field had a value, was explicitly set to null or did not exist in the JSON. Here is what the code might look like:
class DtoConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType.IsClass &&
objectType.GetInterfaces().Any(i => i == typeof(IHasFieldStatus)));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObj = JObject.Load(reader);
var targetObj = (IHasFieldStatus)Activator.CreateInstance(objectType);
var dict = new Dictionary<string, FieldDeserializationStatus>();
targetObj.FieldStatus = dict;
foreach (PropertyInfo prop in objectType.GetProperties())
{
if (prop.CanWrite && prop.Name != "FieldStatus")
{
JToken value;
if (jsonObj.TryGetValue(prop.Name, StringComparison.OrdinalIgnoreCase, out value))
{
if (value.Type == JTokenType.Null)
{
dict.Add(prop.Name, FieldDeserializationStatus.WasSetToNull);
}
else
{
prop.SetValue(targetObj, value.ToObject(prop.PropertyType, serializer));
dict.Add(prop.Name, FieldDeserializationStatus.HasValue);
}
}
else
{
dict.Add(prop.Name, FieldDeserializationStatus.WasNotPresent);
}
}
}
return targetObj;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
The above converter will work on any object that implements the IHasFieldStatus interface. (Note that you do not need to implement the WriteJson method in the converter unless you intend to do something custom on serialization as well. Since CanWrite returns false, the converter will not be used during serialization.)
Now, to use the converter in Web API, you need to insert it into the configuration. Add this to your Application_Start() method:
var config = GlobalConfiguration.Configuration;
var jsonSettings = config.Formatters.JsonFormatter.SerializerSettings;
jsonSettings.Converters.Add(new DtoConverter());
If you prefer, you can decorate each DTO with a [JsonConverter] attribute like this instead of setting the converter in the global config:
[JsonConverter(typeof(DtoConverter))]
class FooDTO : IHasFieldStatus
{
...
}
With the converter infrastructure in place, you can then interrogate the FieldStatus dictionary on the DTO after deserialization to see what happened for any particular field. Here is a full demo (console app):
public class Program
{
public static void Main()
{
ParseAndDump("First run", #"{
""field1"": ""my field 1"",
""nested"": {
""num"": null,
""str"": ""blah"",
""dec"": 3.14
}
}");
ParseAndDump("Second run", #"{
""field1"": ""new field value""
}");
ParseAndDump("Third run", #"{
""nested"": null
}");
}
private static void ParseAndDump(string comment, string json)
{
Console.WriteLine("--- " + comment + " ---");
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new DtoConverter());
FooDTO foo = JsonConvert.DeserializeObject<FooDTO>(json, settings);
Dump(foo, "");
Console.WriteLine();
}
private static void Dump(IHasFieldStatus dto, string indent)
{
foreach (PropertyInfo prop in dto.GetType().GetProperties())
{
if (prop.Name == "FieldStatus") continue;
Console.Write(indent + prop.Name + ": ");
object val = prop.GetValue(dto);
if (val is IHasFieldStatus)
{
Console.WriteLine();
Dump((IHasFieldStatus)val, " ");
}
else
{
FieldDeserializationStatus status = dto.FieldStatus[prop.Name];
if (val != null)
Console.Write(val.ToString() + " ");
if (status != FieldDeserializationStatus.HasValue)
Console.Write("(" + status + ")");
Console.WriteLine();
}
}
}
}
Output:
--- First run ---
Field1: my field 1
Nested:
Num: 0 (WasSetToNull)
Str: blah
Bool: False (WasNotPresent)
Dec: 3.14
--- Second run ---
Field1: new field value
Nested: (WasNotPresent)
--- Third run ---
Field1: (WasNotPresent)
Nested: (WasSetToNull)
Fiddle: https://dotnetfiddle.net/xyKrg2
Looking through the Json.NET source, I found that it supports populating bool properties with a suffix of "Specified" to indicate whether or not the property was included in the data:
class MyClass
{
public string Field1 { get; set; }
public Nested Nested { get; set; }
public bool NestedSpecified { get; set; }
}
class Nested
{
public string Nested1 { get; set; }
public string Nested2 { get; set; }
}
Input:
{
"field1": "my field 1",
"nested": {
"nested1": "something",
"nested2": "else"
}
}
Resulting instance:
MyClass { Field1="my field 1", Nested=Nested { Nested1="something", Nested2="else" }, NestedSpecified=true }
Input:
{
"field1": "new field1 value"
}
Resulting instance:
MyClass { Field1="new field1 value", Nested=null, NestedSpecified=false }
Input:
{
"nested": null
}
Resulting instance:
MyClass { Field1=null, Nested=null, NestedSpecified=true }
I can't find this functionality in the Json.NET documentation but it looks like it has been there since 2010.
You could add some metadata to your JSON objects and (most likely) DTOs. It would require additional processing, but is pretty transparent and unambiguously accomplishes what you need (assuming you can name the new field such that you know it won't collide with actual data).
{
"deletedItems": null,
"field1": "my field 1",
"nested": {
"deletedItems": null,
"nested1": "something",
"nested2": "else"
}
}
{
"deletedItems": "nested",
"field1": "new value",
"nested": null
}
Alternatively, you could add an "isDeleted" property per field if your object model accommodates that better, but that sounds like a lot more work than a list of deleted fields.
I don't want to hijack this question but I posted a slightly different approach to this problem here: https://stackoverflow.com/a/31489835/1395758.
The approach is to replace the fields in your deserializable type with a struct that will automatically keep track of values (even null) through an IsSet property.
The most elegant solution I came up with came to me in an epiphany:
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace testJsonDeserializer
{
class Program
{
static void Main(string[] args)
{
// this operator has the password set to meow.
Operator originalOperator = new Operator
{
OperatorGuid = Guid.Parse("3bb1dc84-2963-4921-a567-fb2e7475623d"),
UserName = "noortje#peterhuppertz.net",
Password = "meow",
PropertyThatWillBeNulled = "noortje#peterhuppertz.net",
};
// this json EXPLICITLY sets the PropertyThatWillBeNulled to null, but omits the Password property, making it null IMPLICITLY.
string json =
"{ \"OperatorGuid\":\"3bb1dc84-2963-4921-a567-fb2e7475623d\", \"UserName\": \"noortje#peterhuppertz.net\", \"Email\": null }";
// What a PATCH would want for the target object is to leave implicit Nulls unchanged, but explicit nulls set to null.
Operator patchedOperator = JsonConvert.DeserializeObject<Operator>(json);
// At this stage, our patched operator has the password set to null. We do not want that; we want to keep whatever is stored in originalOperator
Operator opToStore = MapJsonToOperator(patchedOperator, originalOperator, json);
Console.WriteLine("Our patched operator:");
Console.WriteLine($"Guid: {opToStore.OperatorGuid}");
Console.WriteLine($"UserName: {opToStore.UserName}");
Console.WriteLine($"Password: {opToStore.Password}");
Console.WriteLine($"Email: {opToStore.PropertyThatWillBeNulled}");
Console.ReadKey();
}
private static Operator MapJsonToOperator(Operator source, Operator original, string json)
{
Operator result = new Operator
{
OperatorGuid = source.OperatorGuid,
UserName = source.UserName != null
// we check if the source property has a value, if so, we use that value.
? source.UserName
// if it doesn't, we check the Json to see if the value is in there, explicitly set to NULL. If it is, we set it to NULL as well
: (IsNullValueExplicit(json, "UserName") ? null
// if it is not in the json (making it implicitly null), we just leave the value as it was.
: original.UserName),
PropertyThatWillBeNulled = source.PropertyThatWillBeNulled != null
? source.PropertyThatWillBeNulled
: (IsNullValueExplicit(json, "Email") ? null : original.PropertyThatWillBeNulled),
Password = source.Password != null
? source.Password
: (IsNullValueExplicit(json, "Password") ? null : original.Password),
};
return result;
}
static bool IsNullValueExplicit(string json, string fieldName)
{
JToken outer = JToken.Parse(json);
JObject inner = outer.Value<JObject>();
List<string> keys = inner.Properties().Select(p => p.Name).ToList();
return keys.Contains(fieldName);
}
}
public class Operator
{
public Guid OperatorGuid { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string PropertyThatWillBeNulled { get; set; }
}
}
I know, there are a lot of comments in here. Maybe I overexplained... but I thought I'd err on the side of caution.
I'm building a c# class that works with two different data sources. It will load a data source and take a configuration set from a function. Then I want to do several tasks on all properties within the object.
for example.
public String StreetAddress
{
get { return _streetAddress; }
set
{
if (value.Length <= 64)
_streetAddress = value;
else
_streetAddress = value.Substring(0, 1024).Trim();
}
}
public String City
{
get { return _city; }
set
{
if (value.Length <= 128)
_city = value;
else
_city = value.Substring(0, 128).Trim();
}
}
public String State
{
get { return _state; }
set
{
if (value.Length <= 128)
_state = value;
else
_state = value.Substring(0, 128).Trim();
}
}
So that holds the data from one side. I was hoping to be able to store and set a change flag on each property. So if we take State for example. If the person is moved from Texas to Illinois I want to set a bool within that property to note the change then be able to loop over all changes before saving the object to the DB. But I don't see any way to assign another state variable within that property. Is the best way to write another object on top of this to control it or is there another more creative way to store multiple strings within the one property?
If you'd like an OOP way of doing the thing, you can:
Define an interface and a class for holding your property, such as:
interface IPropertySlot
{
bool IsDirty { get; }
void ResetIsDirty();
object UntypedValue { get; }
}
class PropertySlot<T>:IPropertySlot
{
public T Value { get; private set; }
public bool SetValue(T value)
{
if (!Equals(_value, Value))
{
Value = value;
IsDirty = true;
return true;
}
return false;
}
public bool IsDirty { get; private set; }
public void ResetIsDirty()
{
IsDirty = false;
}
public object UntypedValue
{
get { return Value; }
}
}
Store your properties inside your class in a dictionary from String (for name of property) to IPropertySlot and get/set them through a pair of methods:
void SetProperty<T>(string name, T value)
{
IPropertySlot property;
if (!_properties.TryGetValue(name, out property))
{
property = new PropertySlot<T>();
_properties[name] = property;
}
((PropertySlot<T>)property) .SetValue(value);
}
T GetProperty<T>(string name)
{
IPropertySlot property;
if (!_properties.TryGetValue(name, out property))
{
property = new PropertySlot<T>();
_properties[name] = property;
}
return ((PropertySlot<T>)property).Value;
}
Finding the changed properties later is just a matter of going over the _properties.Values and finding which of them are IsDirty.
This approach also gives you a way to add more functionality to your properties in an OO manner (such as raising PropertyChanged/PropertyChanging events, mapping it to DB fields, etc.).
In such a situation I'd prefer an approach external to the Dto implementation.
Implement some unit that would take two instances of a class, and determine all the differences.
Map each property to compare:
static PropertyManager<Dto> manager = new PropertyManager<Dto>()
.Map(x => x.City)
.Map(x => x.StreetAddress);
Use two instances to compute difference:
var a = new Dto{ StreetAddress = "Foo", City = "Bar" };
var b = new Dto{ StreetAddress = "Foo", City = "Baz" };
var differences = manager.ComputeDifferences(a,b).ToList();
if( differences.Any() )
{
Console.WriteLine("Instances differ");
}
foreach (var diff in differences)
{
Console.WriteLine(diff);
}
This sample code prints out:
Instances differ
x.City
Here is a complete code example:
https://dotnetfiddle.net/4sNeoN
I want to create a json string using json.net in C#. But the json array is not created as I expected. Here is my code:
markInfo[] MarkUpdate1=new markInfo[2];
string jsonString = JsonConvert.SerializeObject(new { MarkUpdate =MarkUpdate1 }, Formatting.Indented);
return jsonString;
public class markInfo
{
List<string> FinalMarks = new List<string>();
List<string> EvalMarks = new List<string>();
}
My expected output is :
{
"MarkUpdate":[
{
"FinalMarks":[
{
}
]
},
{
"EvalMarks":[
{
}
]
}
]
}
But it generated the following output :
{
"MarkUpdate": [
null,
null
]
}
You're creating an anonymous type which has a MarkUpdate property which is assigned the value of your array which contains no object instances.
Are you trying to output one instance of a MarkUpdate? in which case remove your array, instantiate your markInfo class and serialize that.
You should also make your FinalMarks and EvalMarks properties, they are also not marked as public.
string jsonString =
JsonConvert.SerializeObject(new MarkInfo(), Formatting.Indented);
return jsonString;
public class MarkInfo
{
private List<string> finalMarks;
private List<string> evalMarks;
public List<string> FinalMarks
{
get { return this.finalMarks ?? (this.finalMarks = new List<string>()); }
set { this.finalMarks = value; }
}
public List<string> EvalMarks
{
get { return this.evalMarks ?? (this.evalMarks = new List<string>()); }
set { this.evalMarks = value; }
}
}
This line:
markInfo[] MarkUpdate1=new markInfo[2];
...creates an array of markInfo but doesn't create the instances; both of the array slots are empty. So at the least you need to create them:
markInfo[] MarkUpdate1=new markInfo[2];
markInfo[0] = new markInfo();
markInfo[1] = new markInfo();
That still won't give you your "expected output", though, because your expected output only has a single entry, but your code is defining two entries.