How to prevent _t and _v when inserting into MongoDB? - c#

I'm utilizing Dictionary. After .insert() there are "_t" and "_v". Two posts here talked about serialization converting to JSON first then BSON. I'm using MongoDB's driver v2.4.3,
mCollection.InsertOne(x);
IMongoCollection<myDoc> mCollection = Db.GetCollection<myDoc>("whatever");
If I do JSON-to-BSON, it complains about can't convert BsonDocument to myDoc. Switching to IMongoCollection<BsonDocument> mCollection = Db.GetCollection<BsonDocument>("whatever"); still get _t and _v.
How to avoid _t and _v?
Here is my code of data type and utilization:
public class myObjForDictionary
{
//...
}
public class myDoc
{
// ... some other elements, then Dictionary
public Dictionary<string, object> myDictionary { get; set; }
}
// to instantiate the
class myClass
{
// define MongoDB connection, etc.
// instantiate myDoc and populate data
var x = new myDoc
{
//...
myDictionary = new Dictionary<string, object>
{
{ "type", "something" },
{ "Vendor", new object[0] },
{ "obj1", //data for myObjForDictionary
}
};
}
}

I think you're looking for the DictionarySerializationOption... which gives you a couple of different options out of the box to determine how your dictionary gets serialized.

You'll need to save some information (_t) of what object the deserializer needs to create when deserializing the object, if not it won't know what to create from the BSON.
Alternatively, you could change the Dictionary<string, object> to Dictionary<string, BsonDocument> and deal with the BsonDocuments directly within your code. This will be very similar to how you use JObject within Newtonsoft.Json.

It also happens when the passed model to mongo is not exactly the type that is defined on the model.
In this case, mongo will add _t as it identifies that you expect to read the inheritor's class type, and not the type that is defined on the model.
For example:
public class ParentClass { }
public class ChildClass : ParentClass { }
public class ModelToInsert
{
public ParentClass property { get; set; }
}
...
// ChildClass is passed instead of ParentClass
collection.InsertOne(new ModelToInsert { property = new ChildClass() });

Related

Unable to deserialize polymorphic dictionary json due to KnownType "__type" issue

I have created a dictionary with polymorphic values in which I have saved a class object. I have successfully serialized the JSON. But I am unable to deserialize it. It gives below error:
Element ':Value' contains data of the ':Sale' data contract. The deserializer has no knowledge of any type that maps to this contract.
If replace the JSON property "__type" with "type" then it works but is unable to recover correct object type. Before serialization it contains an object of my class type but after deserialization it contains a system.object instead.
My code is below:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
class Program
{
static void Main(string[] args)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
dict.Add("employee","john");
dict.Add("sale",new Sale(9,5243));
dict.Add("restaurant",new Restaurant("Cheese Cake Factory", "New York"));
// Console.Write(dict["sale"]);
//Code for JSON
DataContractJsonSerializer js = new DataContractJsonSerializer(typeof(Dictionary<string, object>));
MemoryStream msObj = new MemoryStream();
js.WriteObject(msObj, dict);
msObj.Position = 0;
StreamReader sr = new StreamReader(msObj);
string json = sr.ReadToEnd();
sr.Close();
msObj.Close();
// Decode the thing
Console.Write(json);
Dictionary<string, object> result = new Dictionary<string, object>();
using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Dictionary<string, object>));
result = serializer.ReadObject(stream) as Dictionary<string, object>;
}
}
}
[DataContract]
[KnownType(typeof(Sale))]
public class Sale
{
[DataMember(Name = "SaleId")]
public int SaleId {get; set;}
[DataMember(Name = "Total")]
public int Total{ get; set;}
public Sale(int saleid, int total)
{
SaleId = saleid;
Total = total;
}
public int getTotal()
{
return Total;
}
}
[DataContract(Name = "Restaurant", Namespace="")]
[KnownType(typeof(Restaurant))]
public class Restaurant
{
[DataMember(EmitDefaultValue = false)]
public string Name {get; set;}
[DataMember(EmitDefaultValue = false)]
public string City{get; set;}
public Restaurant(string name, string city)
{
Name = name;
City = city;
}
}
Fiddle link : https://dotnetfiddle.net/CfQxxV
The problem you're having is that you're putting [KnownType(typeof(...))] above sale and Restaurant.
The reason for using KnownType is for conversion between 1 and the other object. So the deserializer doesn't know that Sale is a KnownType of Object. so it can't Convert Object to Sale.
This would only work if all the items in your dictionary share a common parent object like this:
[KnownType(typeof(Sale))]
[KnownType(typeof(Restaurant))]
[KnownType(typeof(Employee))]
[DataContract]
public class SomeObject {
}
[DataContract(Name = "Sale", Namespace="")]
public class Sale : SomeObject
{
//methods + properties + variables
}
[DataContract(Name = "Restaurant", Namespace="")]
public class Restaurant : SomeObject
{
//methods + properties + variables
}
[DataContract(Name = "Employee", Namespace="")]
public class Employee: SomeObject
{
//methods + properties + variables
}
and then use the dictionary as
Dictionary<string, SomeObject> dict = new Dictionary<string, SomeObject>();
You are attempting to serialize a root object Dictionary<string, object> with polymorphic members. The data contract serializers use a whitelisting approach to polymorphism: all polymorphic subtypes encountered during serialization must have been previously declared via the known type mechanism before an instance of that subtype is encountered in the serialization graph.
So, how can that be done using your data model? There are several approaches:
Add [KnownType(typeof(TDerivedObject))] directly to a base type that is statically declared as shown in this answer by Joost K.
This can't work here because the base type is object, which you cannot modify.
Add [KnownType(typeof(TDerivedObject))] to some parent object in the serialization graph.
This looks problematic because your root object type is Dictionary<string, object>, however you could subclass the dictionary to get your desired result:
[KnownType(typeof(Sale))]
[KnownType(typeof(Restaurant))]
public class ObjectDictionary : Dictionary<string, object>
{
}
Then construct and serialize your dictionary using this subclass:
var dict = new ObjectDictionary()
{
{ "employee","john" },
{"sale",new Sale(9,5243) },
{"restaurant",new Restaurant("Cheese Cake Factory", "New York")},
};
DataContractJsonSerializer js = new DataContractJsonSerializer(typeof(ObjectDictionary));
Demo fiddle #1 here.
Configure additional known types in runtime by constructing a serializer using a DataContractJsonSerializerSettings with known types specified in DataContractJsonSerializerSettings.KnownTypes:
var settings = new DataContractJsonSerializerSettings
{
KnownTypes = new [] { typeof(Sale), typeof(Restaurant) },
};
DataContractJsonSerializer js = new DataContractJsonSerializer(typeof(Dictionary<string, object>), settings);
Be sure to configure the serializer in the same way for both serialization and deserialization.
Demo fiddle #2 here.
(For XML use DataContractSerializerSettings.KnownTypes.)
Specify additional known types via configuration file as shown in Additional Ways to Add Known Types.
You are serializing directly, but if you were serializing via WCF you could add ServiceKnownTypeAttribute to your service contract.
It is never necessary to add [KnownType(typeof(TClass))] to TClass itself, so you can remove such attributes from Restaurant and Sale:
[DataContract]
//[KnownType(typeof(Sale))] Remove this
public class Sale
{
// Remainder unchanged
}
For more, see All About KnownTypes by Sowmy Srinivasan.

Serialization when inheriting from Dictionary

I'm using System.Web.Script.Serialization.JavaScriptSerializer to serialize / deserialize a class that extends Dictionary.
The problem is, my custom properties are not being serialized. Here is my class:
public class Test : Dictionary<string, object> {
public ushort Id { get; set; }
public string Name { get; set; }
}
And my code:
var jss = new JavaScriptSerializer();
var test = new Test {
Id = 123,
Name = "test"
};
var json = jss.Serialize(test);
The result in json is an empty json {}
I do not want to depend on Newtonsoft or JSON.Net or any other library.
ADDITIONAL INFO
I just noticed some, hm, peculiarities, when using both dynamic and object:
JavaScriptSerializer defaults any number value to int.
Also, Newtonsoft defaults any number to long.
That can cause casting exceptions in a class using property indexer (as suggested in the accepted answer), for example:
public class Test : Dictionary<string, dynamic> {
public ushort Id { get => this[nameof(Id)]; set => this[nameof(Id)] = value; }
}
The Id property getter will try to implicitly convert int to ushort, which will fail.
ADDITIONAL INFO 2
I just found out so many weird behaviors with Newtonsoft:
I added these attributes to solve the 'long to ushort' problem:
[JsonObject(MemberSerialization.OptIn)]
public class Test : Dictionary<string, dynamic> {
[JsonProperty]
public ushort Id { get => this[nameof(Id)]; set => this[nameof(Id)] = value; }
}
The above works! But when the property is a reference type:
[JsonObject(MemberSerialization.OptIn)]
public class Test : Dictionary<string, dynamic> {
[JsonProperty]
public ushort Id { get => this[nameof(Id)]; set => this[nameof(Id)] = value; }
[JsonProperty]
public Test Child { get => this[nameof(Child)]; set => this[nameof(Child)] = value; }
}
It tries to get the property before serializing it, resulting in a 'key not found exception'. I can't see why it tries to get the property only when it's a reference type, seems like a bug to me...
So you have to do something like this:
public Test Child { get => this.ContainsKey(index) ? this[nameof(Child)] : null; ... }
Just to summarize the comments:
MSDN recommends using JSON.NET even on JavaScriptSerializer's own docs
Composition will allow you to use JavaScriptSerializer in this case (instead of inheritance)
To work with existing data structure (inheritance), you would have to implement your own version of JavaScriptObjectDeserializer (https://referencesource.microsoft.com/#system.web.extensions/Script/Serialization/JavaScriptObjectDeserializer.cs,2f8d1f9fbf43dbfa)
The default serializer only supports attribute to ignore attribute (not include/rename)
To use composition, you would just need to modify your test object like this:
public class Test
{
public ushort Id { get; set; }
public string Name { get; set; }
public Dictionary<string, object> Items { get; set; } = new Dictionary<string, object> {};
}
Then the following code would work fine:
var jss = new JavaScriptSerializer();
var test = new Test
{
Id = 123,
Name = "test",
};
test.Items.Add("A", 1);
var json = jss.Serialize(test);
The output would just be:
{"Id":123,"Name":"test","Items":{"A":1}}
UPDATE: Property Indexer
You could add a default indexer to your class so that the following code would work:
test["A"] = 1;
var result = test["A"];
Here is the code to add for the default indexer:
public object this[string key]
{
get { return this.Items[key]; }
set { this.Items[key] = value; }
}
You could extend this into implementing IDictionary I suppose, but I imagine just working with the composition should be easiest.

c#: how to hide a field which is used only for XML serialization retrocompatibility?

The field is used only during the serialization / deserialization process but I would like to immediately encapsulate it and hide from the class.
Is it possible?
Basically, no.
XmlSerializer only works with public members, so you can't make it internal or private. You can add some attributes to make it less glaring especially in UIs that data-bind:
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public int Foo {get; set; }
but that only masks it. You could also look at IXmlSerializable, but that is a horrible API and most implementations of it are simply buggy - I do not recommend implementing this interface.
But: best practice is that whenever serialization requirements conflict with your model's design: create a dedicated DTO model - one that matches perfectly your chosen serialization library and exists purely for that purpose. And then map between the two. Then you don't have to compromise.
Its not possible with XML-Serialization in C# , if you want to do like that than you should make use of DataContractSerialization, It allows this kind of functionality i.e. you can serialize private field of you object.
Below is possible with DataContractSerialization, I hope you like to try out
[DataContract]
class Person
{
[DataMember]
public string m_name;
[DataMember]
private int m_age;
}
This what I tried when I was learning XML to Linq , and this is wired solution but if you want to try , here i created xml string by using xml to linq
here is my article : Object to XML using LINQ or XmlSerializer
Note : here code field of product class is private field but still you can generate xml string
using System.Collections.Generic;
using System.Xml.Linq;
using System.Linq;
class Program
{
public class Product
{
public Product()
{ }
public Product(string name,int code, List<productType> types)
{
this.Name = name;
this.Code = code;
this.types = types;
}
public string Name { get; set; }
private int Code { get; set; }
public List<productType> types { get; set; }
public string Serialize(List<Product> products)
{
XElement productSer = new XElement("Products",
from c in products
orderby c.Code
select new XElement("product",
new XElement("Code", c.Code),
new XElement("Name", c.Name),
new XElement("Types", (from x in c.types
orderby x.type//descending
select new XElement("Type", x.type))
))
);
return productSer.ToString();
}
}
public class productType
{
public string type { get; set; }
}
public static void Main()
{
List<productType> typ = new List<productType>();
typ.Add((new productType() { type = "Type1" }));
typ.Add((new productType() { type = "Type2" }));
typ.Add((new productType() { type = "Type3" }));
List<Product> products =new List<Product>() { new Product ( "apple", 9,typ) ,
new Product ("orange", 4,typ ),
new Product ("apple", 9 ,typ),
new Product ("lemon", 9,typ ) };
Console.WriteLine(new Product().Serialize(products));
Console.ReadLine();
}
}
Assuming you are using XmlSerializer, then only public fields and properties can be serialized, as explained in Troubleshooting Common Problems with the XmlSerializer:
The serializer examines all public fields and properties of the Type to learn about which types an instance references at runtime. It then proceeds to create C# code for a set of classes to handle serialization and deserialization using the classes in the System.CodeDOM namespace.
So, what are your options? If you are able to construct your XmlSerializer directly, you could make use of the XmlSerializer.UnknownElement event to forward the unknown elements to the object being deserialized for processing.
First, define the following attribute and extension methods:
[System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false)]
public class XmlUnknownElementEventHandlerAttribute : System.Attribute
{
}
public static partial class XmlSerializationHelper
{
public static T LoadFromXml<T>(this string xmlString, XmlSerializer serial = null)
{
serial = serial ?? new XmlSerializer(typeof(T));
serial.UnknownElement += UnknownXmlElementEventHandler;
using (StringReader reader = new StringReader(xmlString))
{
return (T)serial.Deserialize(reader);
}
}
public static void UnknownXmlElementEventHandler(object sender, XmlElementEventArgs e)
{
var obj = e.ObjectBeingDeserialized;
foreach (var method in obj.GetType().BaseTypesAndSelf()
.SelectMany(t => t.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly))
.Where(m => Attribute.IsDefined(m, typeof(XmlUnknownElementEventHandlerAttribute))))
{
method.Invoke(obj, BindingFlags.Public | BindingFlags.NonPublic, null, new object[] { sender, e }, null);
}
}
}
public static class TypeExtensions
{
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
{
while (type != null)
{
yield return type;
type = type.BaseType;
}
}
}
Next, say you have some class like:
public partial class MyClass
{
public string MyValue { get; set; }
}
And some XML containing an element that needs to be post-processed and converted into the current model, e.g. <OldValue>:
<MyClass><OldValue>Hello</OldValue></MyClass>
Then add a method to MyClass that:
Can be private or internal (in full trust) or public;
Has the same signature as XmlElementEventHandler;
Is marked with your custom attribute [XmlUnknownElementEventHandler];
Performs the necessary post-processing on the old element.
And now the unknown element will be forwarded to it when using a serializer constructed by XmlSerializationHelper.LoadFromXml().
E.g., your method might look like:
public partial class MyClass
{
[XmlUnknownElementEventHandler]
void HandleOldElement(object sender, XmlElementEventArgs e)
{
if (e.Element.Name == "OldValue")
{
Debug.WriteLine("{0}: processed property {1} with value {2}", this, e.Element.Name, e.Element.OuterXml);
MyValue = "Old value was: " + e.Element.InnerText;
}
}
}
And you would deserialize as follows:
var model = xmlString.LoadFromXml<MyClass>();
One advantage of this solution is that it doesn't modify the XSD generated for your types in any way.
Sample fiddle. (Note that, because the dotnetfiddle code executes in partial trust, the handlers must be public. That's not necessary in full trust.)

Serialize to JSON Array of object<string, string>

I need my JSON output to be like the following (array of object}:
{
"values":[{"1","one"},{"2","two"},{"3","three"},{"4","four"}]
}
However, when I serialize the following C# class:
public class MyObject
{
public List<string>[] values {get;set}
}
It results in the following (array of array):
{
"values":[["1","one"],["2","two"],["3","three"],["4","four"]]
}
I've tried many variations on this object. Like the following:
public class MyObject
{
public KeyValuePair[] values {get;set}
}
Which gives me (array of KeyValuePair):
{
"values":[{"Key":"1","Value":"one"},{"Key":"2","Value":"two"},{"Key":"3","Value":"three"},{"Key":"4","Value":"four"}]
}
Is there a C# object property which would serialize to an array of json objects which do not have the object property names included?:
{
"values":[{"1","one"},{"2","two"},{"3","three"},{"4","four"}]
}
I may be answering my own question here, but here is what I found. The following will return an array of objects without property names. However, it is a hack and uses the Dictionary Key as the json property name:
public class MyObject
{
public Dictionary<string, string>[] values { get; set; }
}
Which yields the following:
{
"values":[{"1":"one"},{"2":"two"},{"3":"three"},{"4":"four"}]
}

Json.net serialization of custom collection implementing IEnumerable<T>

I have a collection class that implements IEnumerable and I am having trouble deserializing a serialized version of the same. I am using Json.net v 4.0.2.13623
Here is a simplier version of my collection class that illustrates my issue
public class MyType
{
public int Number { get; private set; }
public MyType(int number)
{
this.Number = number;
}
}
public class MyTypes : IEnumerable<MyType>
{
private readonly Dictionary<int, MyType> c_index;
public MyTypes(IEnumerable<MyType> seed)
{
this.c_index = seed.ToDictionary(item => item.Number);
}
public IEnumerator<MyType> GetEnumerator()
{
return this.c_index.Values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public int CustomMethod()
{
return 1; // Just for illustration
}
}
When I serialize it i get the following json
[
{
"Number": 1
},
{
"Number": 2
}
]
But then when i try to deserialize i get the following exception
System.Exception: Cannot create and populate list type MyTypes
Have also tried using serialization hints but have had no success
These are the test function i have used
public void RoundTrip()
{
var _myTypes1 = new MyTypes(new[] { new MyType(1), new MyType(2) });
var _jsonContent = JsonConvert.SerializeObject(_myTypes1, Formatting.Indented);
var _myTypes2 = JsonConvert.DeserializeObject<MyTypes>(_jsonContent);
}
public void RoundTripWithSettings()
{
var _myTypes1 = new MyTypes(new[] { new MyType(1), new MyType(2) });
var _serializationSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Arrays };
var _jsonContent = JsonConvert.SerializeObject(_myTypes1, Formatting.Indented, _serializationSettings);
var _myTypes2 = JsonConvert.DeserializeObject<MyTypes>(_jsonContent, _serializationSettings);
}
Has anybody managed to serialize their own collection objects ?
Thanks in advance
Pat
As of Json.NET 6.0 Release 3 and later (I tested on 8.0.3), this works automatically as long as the custom class implementing IEnumerable<MyType> has a constructor that takes an IEnumerable<MyType> input argument. From the release notes:
To all future creators of immutable .NET collections: If your collection of T has a constructor that takes IEnumerable then Json.NET will automatically work when deserializing to your collection, otherwise you're all out of luck.
Since the MyTypes class in the question has just such a constructor, this now just works. Demonstration fiddle: https://dotnetfiddle.net/LRJHbd.
Absent such a constructor, one would need to add a parameterless constructor and implement ICollection<MyType> (instead of just IEnumerable<MyType>) with a working Add(MyType item) method. Json.NET can then construct and populate the collection. Or deserialize to an intermediate collection as in the original answer.
I believe JSON .NET depends on having a parameterless constructor for the types it deserializes to. From a deserialization point of view it has no idea what to provide to the constrcutor.
As for your collection, again how is it supposed to know how to populate an implementation of IEnumerable<T> which only defines how to enumerate the collection not how to populate it. You should instead deserialize directly to some standard IEnumerable<MyType> (or directly to IEnumerable<MyType> which I believe JSON.NET supports) and then pass it to your constructor.
I believe all of the following should work in this case:
var _myTypes2 = new MyTypes(JsonConvert.DeserializeObject<MyTypes[]>(_jsonContent));
var _myTypes2 = new MyTypes(JsonConvert.DeserializeObject<List<MyTypes>>(_jsonContent));
var _myTypes2 = new MyTypes(JsonConvert.DeserializeObject<IEnumerable<MyTypes>>(_jsonContent));
Alternatively, though more work, if you need to deserialized directly you can implement something like IList on your collection which should allow JSON.NET to use the IList methods to populate your collection.

Categories

Resources