Serializing a custom object using SOLID principles - c#

I want to serialize my model objects (from WPF MVVM) which contains pure data. This sounds easy but I don't want to use the Serialization attributes and stuff provided in .NET framework. I just want to serialize it using my own way.
So here's a simplified version of one of my classes.
public class EntryKeyValuePair
{
public EntryKeyValuePair(string key, string value, bool isMultiline = false, bool isMandatory = true, bool isProtected = false)
{
Key = key;
Value = value;
IsMultiline = isMultiline;
IsMandatory = isMandatory;
IsProtected = isProtected;
}
public string Key { get; set; }
public string Value { get; set; }
public bool IsMultiline { get; set; }
public bool IsMandatory { get; set; }
public bool IsProtected { get; set; }
public static EntryKeyValuePair FromXML(XElement element, ICipher cipher)
{
string key = cipher.Decrypt(element.Element(nameof(Key)).Value);
string value = cipher.Decrypt(element.Element(nameof(Value)).Value);
bool isMultiline = bool.Parse(element.Element(nameof(IsMultiline)).Value);
bool isMandatory = bool.Parse(element.Element(nameof(IsMandatory)).Value);
bool isProtected = bool.Parse(element.Element(nameof(IsProtected)).Value);
return new EntryKeyValuePair(key, value, isMultiline, isMandatory, isProtected);
}
public XElement ToXML(ICipher cipher)
{
return new XElement(nameof(EntryKeyValuePair),
new XElement(nameof(Key),cipher.Encrypt(Key)),
new XElement(nameof(Value), cipher.Encrypt(Value)),
new XElement(nameof(IsMultiline), IsMultiline), new XElement(nameof(IsMandatory), IsMandatory),
new XElement(nameof(IsProtected), IsProtected));
}
}
This works quite well. But this violates single responsibility principle and maybe other principles as well. This is also difficult to maintain and extend.
So I wanted to find another way. And here it is:
First I defined an IStringFormatter interface which can format the data to any string data formats like XML and JSON. (Not sure tho)
interface IStringFormatter
{
string Name { get; set; }
Dictionary<string, string> FieldDictionary { get; }
string Format();
}
Here's how the XMLStringFormatter looks like:
class XmlStringFormatter : IStringFormatter
{
public XmlStringFormatter()
{
FieldDictionary = new Dictionary<string, string>();
}
public string Name { get; set; }
public Dictionary<string, string> FieldDictionary { get; }
public string Format()
{
var xElement = new XElement(Name, FieldDictionary.Keys.Select(key => new XElement(key, FieldDictionary[key])));
return xElement.ToString();
}
}
Then I defined an ISerializer to serialize (or rather save) my data objects to the IStringFormatter.
interface ISerializer<T>
{
T DeSerialize(IStringFormatter stringFormatter);
void Serialize(T obj, IStringFormatter stringFormatter);
}
And here is how I "Serialize" EntryKeyValurPair by implementing this:
internal class EntryKeyValurPairSerializer : ISerializer<EntryKeyValuePair>
{
public EntryKeyValuePair DeSerialize(IStringFormatter stringFormatter)
{
Dictionary<string, string> fieldDictionary = stringFormatter.FieldDictionary;
try {
string key = fieldDictionary[nameof(EntryKeyValuePair.Key)];
string value = fieldDictionary[nameof(EntryKeyValuePair.Value)];
bool isMandatory = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsMandatory)]);
bool isProtected = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsProtected)]);
bool isMultiline = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsMultiline)]);
return new EntryKeyValuePair(key, value, isMultiline, isMandatory, isProtected);
}
catch (KeyNotFoundException ex) {
throw new SerializationException(ex);
}
catch (FormatException ex) {
throw new SerializationException(ex);
}
}
public void Serialize(EntryKeyValuePair obj, IStringFormatter stringFormatter)
{
stringFormatter.Name = nameof(EntryKeyValuePair);
Dictionary<string, string> fieldDictionary = stringFormatter.FieldDictionary;
fieldDictionary.Add(nameof(EntryKeyValuePair.Key), obj.Key);
fieldDictionary.Add(nameof(EntryKeyValuePair.Value), obj.Value);
fieldDictionary.Add(nameof(EntryKeyValuePair.IsMandatory), obj.IsMandatory.ToString());
fieldDictionary.Add(nameof(EntryKeyValuePair.IsProtected), obj.IsProtected.ToString());
fieldDictionary.Add(nameof(EntryKeyValuePair.IsMultiline), obj.IsMultiline.ToString());
}
}
Now this works fine. But the problem is when I have a complex type like List<Entry> (where Entry is another data class) in my data classes.
As the IStringFormatter contains a Dictionary<string, string>, I can't just convert a List<Entry> to a string because I don't know what kind of IStringFormatter it wants in the context of ISerializer. How can I fix this? I also want to know if my solution is adhering to SOLID principles. If you can suggest a better solution (NOT the typical .NET serialization), I would appreciate it.

Writing your own serializer might be an interesting task, but I doubt that this is a good idea.
As I understood you want to keep your models clean, without any serialization specific attributes. I guess by "typical .NET serialization" you mean methods included with .Net framework.
For simplicity we will use these simple classes as an example:
class Customer
{
public string Name { get; set; }
public int Age { get; set; }
public List<Order> Orders { get; set; }
}
class Order
{
public int Id { get; set; }
public string Details { get; set; }
}
An easy option would be to use Json.NET:
var customer = new Customer
{
Name = "Darth Vader",
Age = 45,
Orders = new List<Order>
{
new Order { Id = 1, Details = "Order1" },
new Order { Id = 2, Details = "Order2" }
}
};
string json = JsonConvert.SerializeObject(customer);
So as you can see you don't need to add any custom attributes to Customer class. It will work until you want to serialize all public properties.
The resulting JSON will be:
{
"Name": "Darth Vader",
"Age": 45,
"Orders": [
{
"Id": 1,
"Details": "Order1"
},
{
"Id": 2,
"Details": "Order2"
}
]
}
After that you can always deserialize it:
var customer = JsonConvert.DeserializeObject<Customer>(json);
Lets say that you don't want Age property to be included. In this case I would suggest to create a different class that will be used for serialization only:
class CostomerSerializationContract
{
public string Name { get; set; }
public List<Order> Orders { get; set; }
}
This main advantage if this approach is that you have separate class for serialization, and you can add any custom attributes there, if you choose to use some other serializer, without violating SOLID principle. The main disadvantage is that you need to keep both objects in sync manually.
You can use AutoMapper to reduce manual work when creating serialization contract from source class.

Related

Deserialize Json an access the result

I have this Json:
{
"UpdatePack":"updatePacks\/1585654836.pack",
"Updates":[
{
"Name":"MsgBoxEx",
"version":"1.5.14.88",
"ChangeLog":"BugFix: Form didn't resize correct.",
"Hash":"5FB23ED83693A6D3147A0485CD13288315F77D3D37AAC0697E70B8F8C9AA0BB8"
},
{
"Name":"Utilities",
"version":"2.5.1.58",
"ChangeLog":"StringManagement updated.",
"Hash":"05E6B3F521225C604662916F50A701E9783E13776DE4FCA27BE4B69705491AC5"
}
]
}
I have created 2 classes to be used to Deserialize it.
class UpdatesList
{
public string Name { get; set; }
public string Version { get; set; }
public string ChangeLog { get; set; }
public string Hash { get; set; }
}
class JsonObjectHolder
{
public string UpdatePack { get; set; }
//public Dictionary<int, MyData> { get; set; }
public Dictionary<int, UpdatesList> Updates { get; set; }
}
But when I try to access the dictionary, I keep getting Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object. at " Console.WriteLine(jsonTest.Dict.Count);"
Am I Deserializing it wrong, or do I need to do some thing else to access the result of the dictionary?
I'm new to both C# and Json.
I hope that some one could point me in the right direction on how to handle this.
I'm using Visual Studio 2019 latest update, and .net 4.8.
Regards
/LR
You code doesn't work because 0 and 1 tokens just a properties, not the array items (you don't have square brackets [] around them). You can parse these values to desired structure manually using JObject
var json = JObject.Parse(your_json_string);
var dict = new Dictionary<int, UpdatesList>();
foreach (var item in json.Properties())
{
if (item.Value.Type == JTokenType.Object)
{
var index = int.Parse(item.Name);
var updateList = item.Value.ToObject<UpdatesList>();
dict.Add(index, updateList);
}
}
var holder = new JsonObjectHolder
{
UpdatePack = json["Updates"]?.Value<string>(),
Dict = dict
};
Update: According to OP changes made to JSON it might be deserialized even more simply
var list = json["Updates"]?.ToObject<List<UpdatesList>>();
var holder = new JsonObjectHolder
{
UpdatePack = json["UpdatePack"]?.Value<string>(),
Dict = list.Select((updatesList, index) => new { updatesList, index })
.ToDictionary(x => x.index, x => x.updatesList)
};
The main point here is that Updates is an array of items, not the key-value collection. It can be transformed into Dictionary<int, UpdatesList> using ToDictionary method from System.Linq (or just use List<UpdatesList> as is)
The exception you're getting essentially means the value is being accessed before the object is initialized.
A better, simpler and cleaner way to doing it is using NewtonSoft. (you can easily get it as a Nuget package)
example:
public class Account
{
public string Email { get; set; }
public bool Active { get; set; }
public DateTime CreatedDate { get; set; }
public IList<string> Roles { get; set; }
}
and then usage:
string json = #"{
'Email': 'james#example.com',
'Active': true,
'CreatedDate': '2013-01-20T00:00:00Z',
'Roles': [
'User',
'Admin'
]
}";
Account account = JsonConvert.DeserializeObject<Account>(json);
Console.WriteLine(account.Email);
Source: https://www.newtonsoft.com/json/help/html/DeserializeObject.htm
I don't see why you need Dictionary<int, UpdatesList> Updates, when you can easily just use List<Update> Updates, since your updates are in a JSON array.
I would model your classes like this:
public class Update
{
public string Name { get; set; }
public string Version { get; set; }
public string ChangeLog { get; set; }
public string Hash { get; set; }
}
public class RootObject
{
public string UpdatePack { get; set; }
public List<Update> Updates { get; set; }
}
You can then deserialize with:
JsonConvert.DeserializeObject<RootObject>(json);
Try it out on dotnetfiddle.net
Note: To convert JSON to C# classes, you can go to Edit -> Paste Special -> Paste JSON as Classes inside Visual Studio. Make sure you have copied the JSON to your clipboard before using it. You will get classes similar to above.
your data and the class is not compatible. if you change the string like this it would work.
change "Updates" to "UpdatePack" and add "Dict" around the dictionary items.
{
"UpdatePack":"updates\/4D1D7964D5B88E5867324F575B77D2FA.zip",
"Dict":{
"0":{
"Name":"MsgBoxEx",
"Version":"1.0.123.58",
"ChangeLog":"Bugfix:Form didn't resize correct",
"hash":"AA94556C0D2C8C73DD217974D252AF3311A5BF52819B06D179D17672F21049A6"
},
"1":{
"Name":"Utilities",
"Version":"1.5.321.87",
"ChangeLog":"StringManagement updated",
"hash":"2F561B02A49376E3679ACD5975E3790ABDFF09ECBADFA1E1858C7BA26E3FFCEF"
}
}
}

Deserialize JSON to 2 different models

Does Newtonsoft.JSON library have a simple way I can automatically deserialize JSON into 2 different Models/classes?
For example I get the JSON:
[{
"guardian_id": "1453",
"guardian_name": "Foo Bar",
"patient_id": "938",
"patient_name": "Foo Bar",
}]
And I need de-serialize this to the following models:
class Guardian {
[JsonProperty(PropertyName = "guardian_id")]
public int ID { get; set; }
[JsonProperty(PropertyName = "guardian_name")]
public int Name { get; set; }
}
class Patient {
[JsonProperty(PropertyName = "patient_id")]
public int ID { get; set; }
[JsonProperty(PropertyName = "patient_name")]
public int Name { get; set; }
}
Is there a simple way to deserialize this JSON into 2 Models without having to iterate over the JSON? Maybe JSON property ids will just work?
Pair<Guardian, Patient> pair = JsonConvert.DeserializeObject(response.Content);
First off, your models are slightly incorrect. The name properties need to be strings, instead of integers:
class Guardian
{
[JsonProperty(PropertyName = "guardian_id")]
public int ID { get; set; }
[JsonProperty(PropertyName = "guardian_name")]
public string Name { get; set; } // <-- This
}
class Patient
{
[JsonProperty(PropertyName = "patient_id")]
public int ID { get; set; }
[JsonProperty(PropertyName = "patient_name")]
public string Name { get; set; } // <-- This
}
Once you've corrected that, you can deserialize the JSON string into two lists of different types. In your case, List<Guardian> and List<Patient> respectively:
string json = #"[{'guardian_id':'1453','guardian_name':'Foo Bar','patient_id':'938','patient_name':'Foo Bar'}]";
var guardians = JsonConvert.DeserializeObject<List<Guardian>>(json);
var patients = JsonConvert.DeserializeObject<List<Patient>>(json);
It you want to do it with 1 call, you need to create a class that matches the JSON. That class can then return Guardian and Patient objects as needed. Also you'll need to use an array or list for the return type because the source JSON is an array.
The class to create:
public class Pair
{
public Pair()
{
Guardian = new Guardian();
Patient = new Patient();
}
[JsonIgnore]
public Guardian Guardian { get; set; }
[JsonIgnore]
public Patient Patient { get; set; }
[JsonProperty(PropertyName = "guardian_id")]
public int GuardianID
{
get { return Guardian.ID; }
set { Guardian.ID = value; }
}
[JsonProperty(PropertyName = "guardian_name")]
public string GuardianName
{
get { return Guardian.Name; }
set { Guardian.Name = value; }
}
[JsonProperty(PropertyName = "patient_id")]
public int PatientID
{
get { return Patient.ID; }
set { Patient.ID = value; }
}
[JsonProperty(PropertyName = "patient_name")]
public string PatientName
{
get { return Patient.Name; }
set { Patient.Name = value; }
}
}
And how to use it:
var pairs = JsonConvert.DeserializeObject<Pair[]>(response.Content);
if (pairs.Any())
{
var pair = pairs[0];
Console.WriteLine(pair.Guardian.Name);
Console.WriteLine(pair.Patient.Name);
}
Not in one call, and it seems the data is an array, so you need a little more work.
Zip is the key method here to join the two separate object lists:
Guardian[] guardians = JsonConvert.DeserializeObject<Guardian[]>(response.Content);
Patient[] patients = JsonConvert.DeserializeObject<Patient[]>(response.Content);
var combined = guardians.Zip(patients, (g, p) => Tuple.Create(g, p)).ToList();
It would be far more easier to just read the JSON at once, it a single object.
It can't be done with 1 call with the types that you show. You can try using the generic <T> approach for each type, also you'll need to use arrays or lists for the return type because the source JSON is an array:
var guardians = JsonConvert.DeserializeObject<Guardian[]>(response.Content);
var patients = JsonConvert.DeserializeObject<Patient[]>(response.Content);
And then combine the two if you need them to be paired. E.g. if you are sure that you always have just one of each:
var pair = new Pair(guardians[0], patients[0]);
You could make a type to house the two subobjects:
[JsonConverter(typeof(GuardianPatientConverter))]
class GuardianPatient
{
public Guardian Guardian { get; set; }
public Patient Patient { get; set; }
}
And then create a JSON converter to handle the JSON:
class GuardianPatientConverter : JsonConverter
{
public override bool CanRead
{
get { return true; }
}
public override bool CanWrite
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return typeof(GuardianPatient) == objectType;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
var jObject = JObject.Load(reader);
var guardian = new Guardian();
var patient = new Patient();
serializer.Populate(jObject.CreateReader(), guardian);
serializer.Populate(jObject.CreateReader(), patient);
return new GuardianPatient()
{
Guardian = guardian,
Patient = patient
};
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And then you can use it like so:
var json = "[{\"guardian_id\":\"1453\",\"guardian_name\":\"Foo Bar\",\"patient_id\":\"938\",\"patient_name\":\"Foo Bar\",}]";
var objects = JsonConvert.DeserializeObject<IEnumerable<GuardianPatient>>(json);
and if you want it as an array of pairs:
var objects = JsonConvert.DeserializeObject<IEnumerable<GuardianPatient>>(json)
.Select(o => new Pair(o.Guardian, o.Patient))
.ToArray();
This won't make it any faster, but I suspect you're looking for an easier way to work with the JSON.
One another approach would be creating class that matches JSON format, i.e. class with four properties with corresponding names. Then, deserialize JSON into that class and then use it in your code (set properties of objects with values from JSON, pass deserialized object to constructor of another class).
In your models, The name properties need to be strings, instead of integers. After correcting it.
You can use Tuple class
string json = #"[{'guardian_id':'1453','guardian_name':'Foo Bar','patient_id':'938','patient_name':'Foo Bar'}]";
var combination = new Tuple<List<Guardian>, List<Patient>>(JsonConvert.DeserializeObject<List<Guardian>>(json), JsonConvert.DeserializeObject<List<Patient>>(json));
static void Main(string[] args)
{
string json = JsonConvert.SerializeObject(new[]
{
new
{
guardian_id = "1453",
guardian_name = "Foo Bar",
patient_id = "938",
patient_name = "Bar Foo",
}
});
Guardian[] guardians = JsonConvert.DeserializeObject<Guardian[]>(json);
Patient[] patients = JsonConvert.DeserializeObject<Patient[]>(json);
}
Since both your objects are the same, wouldn't it make more sense to just have an ID/Name structure of a single base class? If you need to send all the data at the same time, you can restructure your data and use a data transfer object pattern. The JSON object would become
[{
"guardian": {
"id": "1453",
"name": "Foo Bar"
},
"patient": {
"id" : "938",
"name": "Foo Bar"
}
}]
And your corresponding data objects would be:
public class Record {
public int id { get; set; } // or string. I'm not sure which would be more appropriate
public string name { get; set;}
}
and
public class RecordDto {
public Record guardian { get; set; }
public Record patient { get; set; }
}
And your API would receive a
List<RecordDto>
parameter (since you are passing an array of objects).

Derserialize JSON.NET With Unknown RootObject Key

{
"578080": {
"success": true,
"data": {
"type": "game",
"name": "PLAYERUNKNOWN'S BATTLEGROUNDS",
"steam_appid": 578080,
"required_age": 0,
"is_free": false,
}
}
}
This is from the Steam API. As you can see the root key the ID itself, so I don't know how to deserialize this to an object. I've seen other questions regarding unknown property names, but can't seem to apply those solutions for when the root name is unknown.
One way to do this is to Deserialize to Dictionary
Classes
public class Data
{
public string type { get; set; }
public string name { get; set; }
public int steam_appid { get; set; }
public int required_age { get; set; }
public bool is_free { get; set; }
}
public class SomeClass
{
public bool success { get; set; }
public Data data { get; set; }
}
Usage
var result = JsonConvert.DeserializeObject<Dictionary<string, SomeClass>>(json);
If you don't care about making POCO models for your deserialized data and just want to grab some of the properties using a dynamic, you can use JsonExtensionData to get a JToken of the relevant subobject:
public class Foo
{
[JsonExtensionData]
public Dictionary<string, JToken> ExtensionData {get; set;}
}
dynamic obj = JsonConvert.DeserializeObject<Foo>(json).ExtensionData.Single().Value;
Console.WriteLine(obj.success);
Console.WriteLine(obj.data.name);
This approach would be particularly useful if you could reuse Foo across several different types of responses since it doesn't care at all about the object schema.
You can use an anonymous type deserialization to parse JSON data like this, without creating classes. I assumed there is only one Id("578080") present in your data.If more Id's present, you can create an array for those Id's. Hope It Works.
var finalResult=JsonConvert.DeserializeAnonymousType(
yourdata, // input
new
{
Id=
{
new
{
success="", data=""
}
}
}
);
console.write(finalResult.Id);// getting Id 578080
console.write(finalResult.Id.success);
console.write(finalResult.Id.data.type);

Convert class to dynamic and add properties

I have a class MyClass. I would like to convert this to a dynamic object so I can add a property.
This is what I had hoped for:
dynamic dto = Factory.Create(id);
dto.newProperty = "123";
I get the error:
WEB.Models.MyClass does not contain a definition for 'newProperty'
Is that not possible?
The following has worked for me in the past:
It allows you to convert any object to an Expando object.
public static dynamic ToDynamic<T>(this T obj)
{
IDictionary<string, object> expando = new ExpandoObject();
foreach (var propertyInfo in typeof(T).GetProperties())
{
var currentValue = propertyInfo.GetValue(obj);
expando.Add(propertyInfo.Name, currentValue);
}
return expando as ExpandoObject;
}
Based on: http://geekswithblogs.net/Nettuce/archive/2012/06/02/convert-dynamic-to-type-and-convert-type-to-dynamic.aspx
As my object has JSON specific naming, I came up with this as an alternative:
public static dynamic ToDynamic(this object obj)
{
var json = JsonConvert.SerializeObject(obj);
return JsonConvert.DeserializeObject(json, typeof(ExpandoObject));
}
For me the results worked great:
Model:
public partial class Settings
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("runTime")]
public TimeSpan RunTime { get; set; }
[JsonProperty("retryInterval")]
public TimeSpan RetryInterval { get; set; }
[JsonProperty("retryCutoffTime")]
public TimeSpan RetryCutoffTime { get; set; }
[JsonProperty("cjisUrl")]
public string CjisUrl { get; set; }
[JsonProperty("cjisUserName")]
public string CjisUserName { get; set; }
[JsonIgnore]
public string CjisPassword { get; set; }
[JsonProperty("importDirectory")]
public string ImportDirectory { get; set; }
[JsonProperty("exportDirectory")]
public string ExportDirectory { get; set; }
[JsonProperty("exportFilename")]
public string ExportFilename { get; set; }
[JsonProperty("jMShareDirectory")]
public string JMShareDirectory { get; set; }
[JsonIgnore]
public string Database { get; set; }
}
I used it in this manner:
private static dynamic DynamicSettings(Settings settings)
{
var settingsDyn = settings.ToDynamic();
if (settingsDyn == null)
return settings;
settingsDyn.guid = Guid.NewGuid();
return settingsDyn;
}
And received this as a result:
{
"id": 1,
"runTime": "07:00:00",
"retryInterval": "00:05:00",
"retryCutoffTime": "09:00:00",
"cjisUrl": "xxxxxx",
"cjisUserName": "xxxxx",
"importDirectory": "import",
"exportDirectory": "output",
"exportFilename": "xxxx.xml",
"jMShareDirectory": "xxxxxxxx",
"guid": "210d936e-4b93-43dc-9866-4bbad4abd7e7"
}
I don't know about speed, I mean it is serializing and deserializing, but for my use it has been great. A lot of flexability like hiding properties with JsonIgnore.
Note: xxxxx above is redaction. :)
You cannot add members to class instances on the fly.
But you can use ExpandoObject. Use factory to create new one and initialize it with properties which you have in MyClass:
public static ExpandoObject Create(int id)
{
dynamic obj = new ExpandoObject();
obj.Id = id;
obj.CreatedAt = DateTime.Now;
// etc
return obj;
}
Then you can add new members:
dynamic dto = Factory.Create(id);
dto.newProperty = "123";
You can't add properties to types at runtime. However, there is an exception which is: ExpandoObject. So if you need to add properties at runtime you should use ExpandoObject, other types don't support this.
Just to add up my experience, if you are using JSON.NET, then below might be one of the solution:
var obj....//let obj any object
ExpandoObject expandoObject= JsonConvert.DeserializeObject<ExpandoObject>(JsonConvert.SerializeObject(obj));
Not tested performances etc.. but works.

C# How to serialize/deserialize an Object to/from Dictionary<string,Object> to save/load it in Couchbase.lite

If I want to store data as Document in Clouchbase.lite is has to be in the form of Dictionary like this:
var properties = new Dictionary<string, object>()
{
{"type", "list"},
{"title", "title"},
{"created_at", DateTime.UtcNow.ToString ("o")},
{"owner", "profile:" + userId},
{"members", new List<string>()}
};
var rev = document.PutProperties(properties);
How can I automatically create such a Dictionary from any C# object?
If I understand the couchbase.lite documentation this process does not to be recursive, just on the first level of my object fields and properties.
How can I recreate my objects from such a dictionary?
Ok, this not a full answer to my question, but it solves the problem for now. I came to the solution of putting the whole object into the Dictionary that is handed to Couchbase.Lite:
var c = new TestClass() {TestString = "SimpleStringfield", TestDate = DateTime.Today,TestStringProperty = "TestStringPropertyContent",
TestStringList = new List<string>(new string[] { "item1","item2","item3"})};
var manager = Manager.SharedInstance;
var db = manager.GetDatabase("test_database");
if (db == null)
{
System.Diagnostics.Debug.WriteLine("--------------------------Could not open Database");
}
var doc = db.CreateDocument();
var properties = new Dictionary<string,Object>() { {"type","day"}, {"day",c} };
var rev = doc.PutProperties(properties);
var docread = db.GetDocument(doc.Id);
JObject testobject = (JObject)docread.GetProperty("day");
var o = testobject.ToObject<TestClass>();
}
At the end o contains the same values as c.
I want to point out the warning that Jim Borden gave me to this approach:
I will always assert that the "right way" is the way that works. The
fact that the library uses JSON .NET internally is an implementation
detail, though. If that were to change for some reason then this way
would break. I also have a feeling that if the class were more complex
than your example than it would break as well. We are currently
designing a cross platform specification to do just what you want to
do (save classes directly to the database without converting to a
dictionary first). This will be a slow process though because there is
a lot of thought that needs to go into it (not just for C#, but for
Java and Objective-C as well). For example, how to serialize a class
that has a property which references back to it, etc. I know JSON .NET
already does this, but I don't want to start exposing JSON .NET into
the public API because eventually it should be switchable (in case
JSON .NET does not work for whatever reason on a platform).
I know you are talking about automatic, but I needed the individual fields of my class to be exposed so I can index them and run mapped queries. So I specified all my query-able objects to follow this interface:
public interface IEntity
{
string Id { get; set; }
IDictionary<string, object> ToDictionary();
void PopulateFrom(IDictionary<string, object> prop);
}
And typically implemented as follows:
public class Club : IEntity
{
public string Id { get; set; }
public string Name { get; set; }
public string Country { get; set; }
public string Place { get; set; }
public long ClubId { get; set; }
public string Access { get; set; } = "read";
public List<ClubMember> Members { get; set; }
public void PopulateFrom(IDictionary<string, object> prop)
{
Name = prop.GetOrDefault<string>("name");
Country = prop.GetOrDefault<string>("country");
Place = prop.GetOrDefault<string>("place");
ClubId = prop.GetOrDefault<long>("club_id");
Access = prop.GetOrDefault<string>("access");
Members = ParseMembers(prop["members"]);
}
public IDictionary<string, object> ToDictionary()
{
return new Dictionary<string, object>
{
{ "club_id", ClubId },
{ "name", Name },
{ "country", Country },
{ "place", Place },
{ "access", Access },
{ "members", Members}
};
}
}

Categories