Json.Net cannot serialize property of a class derived from dictionary - serialization

I have a class that is derived from Dictionary. Also the class itself has a ClientId property. I want to de-serialize the collection of this class into JSON string using Json.Net So followed the documentation here
public interface IClientSettings: IDictionary<string, string>
{
string ClientId { get; set; }
}
public class ClientSettings : Dictionary<string, string>, IClientSettings
{
public string ClientId { get; set; }
}
and then I am de-serializing list into string
var list = new List<IClientSettings>();
var client1 = new ClientSettings();
client1.ClientId = "Client1";
client1.Add("key1", "value1");
client1.Add("key2", "value2");
client1.Add("key3", "value3");
var client2 = new ClientSettings();
client1.ClientId = "Client2";
client2.Add("key1", "value1");
client2.Add("key2", "value2");
client2.Add("key3", "value3");
list.Add(client1);
list.Add(client2);
string json = JsonConvert.SerializeObject(list, Formatting.Indented);
Console.WriteLine(json);
However this does not serialize Clientid property. Below is the output.
[
{
"key1": "value1",
"key2": "value2",
"key3": "value3"
},
{
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
]
I am not sure what I am missing here. I have also found suggestion here that suggest to do custom serialization, is that the only option I have?

Yes, you are correct, you will need to do some sort of custom serialization. Json.NET will either serialize the key/value pairs of the dictionary as a JSON object (using JsonDictionaryContract), or the properties of the dictionary as a JSON object (using JsonObjectContract) if you mark the type with [JsonObject] - but not both. I suspect doing both was not implemented to thereby avoid the possibility of run-time name clashes when the dictionary contains a key with the same name as a property, e.g.:
var client3 = new ClientSettings();
client3.ClientId = "Client1";
client3["ClientId"] = "Conflicting Value";
According to the IETF standard,
When the names within an object are not unique, the behavior of software that receives such an object is unpredictable.
Thus this is a situation best avoided.
One possible implementation is as follows:
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class ClientSettings : Dictionary<string, string>, IClientSettings
{
[JsonProperty]
public string ClientId { get; set; }
[JsonProperty]
IDictionary<string, string> Items { get { return new DictionaryWrapper<string, string>(this); } }
}
Using
public class DictionaryWrapper<TKey, TValue> : IDictionary<TKey, TValue>
{
readonly IDictionary<TKey, TValue> dictionary;
public DictionaryWrapper(IDictionary<TKey, TValue> dictionary)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
this.dictionary = dictionary;
}
#region IDictionary<TKey,TValue> Members
public void Add(TKey key, TValue value) { dictionary.Add(key, value); }
public bool ContainsKey(TKey key) { return dictionary.ContainsKey(key); }
public ICollection<TKey> Keys { get { return dictionary.Keys; } }
public bool Remove(TKey key) { return dictionary.Remove(key); }
public bool TryGetValue(TKey key, out TValue value) { return dictionary.TryGetValue(key, out value); }
public ICollection<TValue> Values { get { return dictionary.Values; } }
public TValue this[TKey key]
{
get { return dictionary[key]; }
set { dictionary[key] = value; }
}
#endregion
#region ICollection<KeyValuePair<TKey,TValue>> Members
public void Add(KeyValuePair<TKey, TValue> item) { dictionary.Add(item); }
public void Clear() { dictionary.Clear(); }
public bool Contains(KeyValuePair<TKey, TValue> item) { return dictionary.Contains(item); }
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { dictionary.CopyTo(array, arrayIndex); }
public int Count { get { return dictionary.Count; } }
public bool IsReadOnly { get { return dictionary.IsReadOnly; } }
public bool Remove(KeyValuePair<TKey, TValue> item) { return dictionary.Remove(item); }
#endregion
#region IEnumerable<KeyValuePair<TKey,TValue>> Members
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return dictionary.GetEnumerator(); }
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
#endregion
}
MemberSerialization.OptIn is used to prevent base class properties such as Count, Comparer, Keys and Values from being serialized.
With this, your JSON will look like:
[
{
"ClientId": "Client2",
"Items": {
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
}
]

Related

Extend AspNetCore InputTextArea component to add rows

I would like to extend InputTextArea aspnet-core component to add support for rows and use it in razor pages.
The way I was thinking of doing it is to create a new class and inherit from InputBase<string> and add a rows attribute but I do not know how to implement it further.
public class TextAreaWithRows : InputBase<string>
{
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "textarea");
builder.AddAttribute(1, "id", Id);
builder.AddAttribute(2, "class", CssClass);
builder.AddAttribute(3, "value", BindMethods.GetValue(CurrentValue));
builder.AddAttribute(4, "onchange", BindMethods.SetValueHandler(__value => CurrentValue = __value, CurrentValue));
builder.AddAttribute(5, "rows", );
builder.CloseElement();
}
protected override bool TryParseValueFromString(string value, out string result, out string validationErrorMessage)
{
result = value;
validationErrorMessage = null;
return true;
}
}
Add a Parameter Property for Rows to the class.
public class TextAreaWithRows : InputBase<string> {
//For Attributes
[Parameter] public int Rows { get; set; }
//For Events
[Parameter] public Action OnInput { get; set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "textarea");
builder.AddAttribute(1, "id", Id);
builder.AddAttribute(2, "class", CssClass);
builder.AddAttribute(3, "value", BindMethods.GetValue(CurrentValue));
builder.AddAttribute(4, "onchange", BindMethods.SetValueHandler(__value => CurrentValue = __value, CurrentValue));
builder.AddAttribute(5, "rows", Rows);
builder.AddAttribute(6, "oninput", EventCallback.Factory.Create<UIEventArgs>(this, OnInput));
builder.CloseElement();
}
protected override bool TryParseValueFromString(string value, out string result, out string validationErrorMessage)
{
result = value;
validationErrorMessage = null;
return true;
}
}

MVC RequiredIf Attribute - IsValid value parameter always null

I am implementing a RequiredIf validation attribute and the value being passed to the IsValid method is always null.
RequiredIfAttribute Class
public class RequiredIfAttribute : ValidationAttribute
{
private RequiredAttribute innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
}
ViewModel
[Required]
[Display(Name = "Are You A Student?")]
public bool? IsStudent { get; set; }
[RequiredIf("IsStudent", true, ErrorMessage = "You must upload a photo of your student ID if you wish to register as a student.")]
[Display(Name = "Student ID")]
[FileExtensions("jpg|jpeg|png|pdf", ErrorMessage = "File is not in the correct format.")]
[MaxFileSize(2 * 1024 * 1024, ErrorMessage = "You may not upload files larger than 2 MB.")]
public HttpPostedFileBase StudentId { get; set; }
EditorTemplate
#model bool?
#using System.Web.Mvc;
#{
var options = new List<SelectListItem>
{
new SelectListItem { Text = "Yes", Value = "true", Selected = Model.HasValue && Model.Value },
new SelectListItem { Text = "No", Value = "false", Selected = Model.HasValue && Model.Value }
};
string defaultOption = null;
if (ViewData.ModelMetadata.IsNullableValueType)
{
defaultOption = "(Select)";
}
}
#Html.DropDownListFor(m => m, options, defaultOption)
Every time the form is submitted, the RequiredIf error message is thrown and I have a feeling it has to do with the null value I described initially. What am I doing wrong? Thanks!
NOTE: The HTML appears to be rendering properly, so I don't think that's the problem.
<select data-val="true" data-val-required="The Are You A Student? field is required." id="IsStudent" name="IsStudent"><option value="">(Select)</option>
<option value="true">Yes</option>
<option value="false">No</option>
</select>
Because this is your code -
public class RequiredIfAttribute : ValidationAttribute
{
private RequiredAttribute innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
}
You are using a RequriedAtrribute. So to simulate that it behaves life a RequiredIf you have to implement some logic that will check whether the target property value is true or false. But you are not doing that and returning just from the innerattribute. So it is just a mere Required not RequiredIf -
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
modify this function to do some checking like -
public override bool IsValid(object value)
{
//if the referred property is true then
return innerAttribute.IsValid(value);
//else
return True
}
I use the following code:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public abstract class StefAttribute : ValidationAttribute
{
public WDCIAttribute()
: base()
{
this.ErrorMessageResourceType = typeof(GlobalResources);
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class StefRequiredIfAttribute : StefAttribute
{
private RequiredAttribute innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public WDCIRequiredIfAttribute()
{
}
public WDCIRequiredIfAttribute(string dependentProperty, object targetValue)
: base()
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
}
public class RequiredIfValidator : DataAnnotationsModelValidator<StefRequiredIfAttribute>
{
public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, StefRequiredIfAttribute attribute)
: base(metadata, context, attribute)
{
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
// get a reference to the property this validation depends upon
var field = Metadata.ContainerType.GetProperty(Attribute.DependentProperty);
if (field != null)
{
// get the value of the dependent property
object value = field.GetValue(container, null);
// compare the value against the target value
if (IsEqual(value) || (value == null && Attribute.TargetValue == null))
{
// match => means we should try validating this field
if (!Attribute.IsValid(Metadata.Model))
{
// validation failed - return an error
yield return new ModelValidationResult { Message = ErrorMessage };
}
}
}
}
private bool IsEqual(object dependentPropertyValue)
{
bool isEqual = false;
if (Attribute.TargetValue != null && Attribute.TargetValue.GetType().IsArray)
{
foreach (object o in (Array)Attribute.TargetValue)
{
isEqual = o.Equals(dependentPropertyValue);
if (isEqual)
{
break;
}
}
}
else
{
if (Attribute.TargetValue != null)
{
isEqual = Attribute.TargetValue.Equals(dependentPropertyValue);
}
}
return isEqual;
}
}
Which can be used in the model as follows:
public class PersonnelVM : EntityVM
{
// . . .
[DisplayName("Name")]
[StefRequiredIf("IndividualOrBulk", PersonnelType.Bulk, ErrorMessageResourceName = GlobalResourceLiterals.Name_Required)]
public string Name { get; set; }
[DisplayName("PersonnelType")]
public PersonnelType IndividualOrBulk { get; set; }
// . . .
}

NHibernate - Is it possible to map columns into collection composite objects

Using NHibernate is it possible to map columns in a table to a collection of objects.
For example if I have a very badly designed database table with columns as such:
ClientID
ClientName
First_AmountPaid
Second_AmountPaid
Third_AmountPaid
Fourth_AmountPaid
Is it possible to map this to the following class structure where First_AmountPaid through to Fourth_AmountPaid have their own class implementation?
public class Client
{
public int ClientId { get; set; }
public string ClientName { get; set; }
public IList<AmountPaid> Amounts { get; set; }
}
public class AmountPaid
{
public decimal Amount { get; set; }
}
public class FirstAmountPaid : AmountPaid{ }
public class SecondAmountPaid : AmountPaid{ }
public class ThirdAmountPaid : AmountPaid{ }
public class FourthAmountPaid : AmountPaid{ }
Therefore giving a more meaningful code structure.
Thank you
i'm not sure why there are subclasses when the listposition already defines the order of the amounts
Map(x => x.Amounts)
.Columns.Add("First_AmountPaid", "Second_AmountPaid", "Third_AmountPaid", "Fourth_AmountPaid")
.CustomType<AmountPaidType>();
class AmountPaid : IUserType
{
public object Assemble(object cached, object owner)
{
return cached;
}
public object DeepCopy(object value)
{
return ((IList<AmountPaid>)x).Select(a => a.Clone()).ToList();
}
public object Disassemble(object value)
{
return value;
}
bool IUserType.Equals(object x, object y)
{
// assuming AmountPaid implements Equals
return ((IList<AmountPaid>)x).SequenceEquals((IList<AmountPaid>)y);
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public bool IsMutable
{
get { return true; }
}
public void NullSafeSet(cmd, value, index)
{
var list = (IList<AmountPaid>)value;
NHibernateUtil.Double.NullSafeSet(cmd, list[0].Amount, index);
NHibernateUtil.Double.NullSafeSet(cmd, list[1].Amount, index + 1);
NHibernateUtil.Double.NullSafeSet(cmd, list[2].Amount, index + 2);
NHibernateUtil.Double.NullSafeSet(cmd, list[3].Amount, index + 3);
}
public object NullSafeGet(rs, names, owner)
{
var list = new List<AmountPaid>();
foreach (var name in names)
{
list.Add(new AmountPaid((double)NHibernateUtil.Double.Get(rs, name)));
}
return list;
}
public object Replace(object original, object target, object owner)
{
return original;
}
public Type ReturnedType
{
get { return typeof(IList<AmountPaid>); }
}
public SqlType[] SqlTypes
{
get { return new[] { SqlTypeFactory.Double, SqlTypeFactory.Double, SqlTypeFactory.Double, SqlTypeFactory.Double }; }
}
}

Using discriminator with Fluent NHibernate

I'm trying to create a discriminator column. This column would hold one of the many statuses available. Like my code will show, each status has a name as well as a background color. Each status shares the same base class.
Here is my code:
public class Item
{
public virtual int Id { get; set; }
public virtual Status ItemStatus { get; set; }
}
public abstract class Status
{
private readonly int _id;
public static readonly Status Foo = new FooStatus(1);
public static readonly Status Bar = new BarStatus(2);
public Status()
{
}
protected Status(int id)
{
_id = id;
}
public virtual int Id { get { return _id; } }
public abstract string Name { get; }
public abstract string BackgroundColor { get; }
}
public class FooStatus : Status
{
public FooStatus()
{
}
public FooStatus(int id)
: base(id)
{
}
public override string Name
{
get { return "Foo Status"; }
}
public override string BackgroundColor
{
get { return "White"; }
}
}
public class BarStatus : Status
{
public BarStatus()
{
}
public BarStatus(int id)
: base(id)
{
}
public override string Name
{
get { return "Bar Status"; }
}
public override string BackgroundColor
{
get { return "Black"; }
}
}
And here is my mapping:
public class ItemMap : ClassMap<Item>
{
public ItemMap()
{
Id(x => x.Id).GeneratedBy.Identity();
DiscriminateSubClassesOnColumn<int>("ItemStatus", 0).AlwaysSelectWithValue();
}
}
Essentially, what I'd like is that if I set ItemStatus to Status.Foo then the ItemStatus column would have a value of 1. What I have now doesn't throw any exceptions, but it always inserts ItemStatus as 0.
This is the inserting code I'm using:
using (var session = sessionFactory.OpenSession())
using (var transaction = session.BeginTransaction())
{
var item = new Item
{
ItemStatus = Status.Foo
};
session.Save(item);
transaction.Commit();
var firstItem = session.Get<Item>(1);
Console.WriteLine(firstItem.ItemStatus.Name);
}
Where can I read up on this topic using FNH?
Before anyone suggests be to check on Google I did search several things but nowhere can I find a full example.
Your SubclassMap would look something like this:
public class FooStatusMap : SubclassMap<FooStatus>
{
public FooStatusMap()
{
DiscriminatorValue(1);
}
}
This is called "table-per-class-hierarchy," and you're right it doesn't look like there are many resources on it out there.
I believe if you don't call DiscriminatorValue in a SubclassMap, NHibernate attempts to discriminate by looking at the name of the subclass being mapped and seeing if it matches up with the value in the discriminator column.
I wouldnt write submaps for all the subclasses you can just do this instead
public class FooMap: ClassMap<T>
{
//other mapping
DiscriminateSubClassesOnColumn("DiscriminatorColumn")
.SubClass<Foo1>(m => { })
.SubClass<Foo2>(m => { })
.SubClass<Foo3>(m => { });
}
Hope that helps
If you're open to the Discriminator column having the class names of the derived classes, you can implement this via automapping.
In your session factory:
private static ISessionFactory CreateSessionFactory()
{
var cfg = new MyMappingConfiguration();
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("MyConnectionKey")).FormatSql().ShowSql()
)
.Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Status>(cfg)
.IncludeBase<Status>()
.Conventions.Add<PrimaryKeyConvention>()))
.BuildSessionFactory();
}
Then add the MyMappingConfiguration override:
public class MappingConfiguration : DefaultAutomappingConfiguration
{
public override bool IsId(Member member)
{
return member.Name == member.DeclaringType.Name + "Id";
}
public override bool IsDiscriminated(Type type)
{
return true;
}
}
Hope that h

Can I use NHibernate to store an object in xml serialized form?

Say I have a class like this:
public class MyClass
{
public int Id { get; set; }
public DateTime Date { get; set; }
public string String1 { get; set; }
public string String2 { get; set; }
public string String3 { get; set; }
public string String4 { get; set; }
}
Is it possible to get NHibernate to store it in the following schema?
CREATE TABLE [dbo].[MyClass](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Xml] [varchar](max) NOT NULL,
)
Where the Id maps to Id, but then all other fields get serialized into XML (or otherwise)? I don't mind if these other fields have to go on a child object like the below, if that helps:
public class MyClass
{
public int Id { get; set; }
public AllOtherOptions Options { get; set; }
}
public class AllOtherOptions
{
public DateTime Date { get; set; }
public string String1 { get; set; }
public string String2 { get; set; }
public string String3 { get; set; }
public string String4 { get; set; }
}
I am thinking about doing something similar for an upcoming project. The project requires collecting a lot of data but only a few elements need to be stored in a relational database. I haven't started experimenting but these are my thoughts so far.
You can map an XML data type by creating a type that implements IUserType. If the child class (AllOtherOptions) is serializable, you should be able to map the XML field as a private member in MyClass and serialize/deserialize AllOtherOptions as needed. You could either dynamically maintain the XML field (sounds like a lot of work) or create an interceptor to do it. My thinking is that MyClass would implement an interface such as
public interface IXmlObjectContainer
{
void SerializeChildObjects();
void DeSerializeChildObjects();
}
and the interceptor would call those methods as needed. That's a proof of concept idea. I would probably refine that by exposing pairs of xml fields and serializable objects to remove the work of serializing from IXmlObjectContainer implementers. Or maybe handle serialization through the XML field's get/set accessors.
More info:
Working with XML Fields in NHibernate
Another XML implementation of IUserType
I had the same idea to save object in XML column. My idea was other. I took code from links and changed it to generic IUserType implementation. So any field/prop which is [Serializable] can be saved in XML column.
public class XmlUserType<T> : IUserType where T : class
{
public new bool Equals(object x, object y)
{
return x == y;
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
if (names.Length != 1)
throw new InvalidOperationException("names array has more than one element. can't handle this!");
var val = rs[names[0]] as string;
if (string.IsNullOrWhiteSpace(val) == false)
{
return KRD.Common.GenericXmlSerialization.Deserialize<T>(val);
}
return null;
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
var parameter = (DbParameter)cmd.Parameters[index];
T toSave = value as T;
if (toSave != null)
{
parameter.Value = KRD.Common.GenericXmlSerialization.Serialize(toSave);
}
else
{
parameter.Value = DBNull.Value;
}
}
public object DeepCopy(object value)
{
T toCopy = value as T;
if (toCopy == null)
return null;
string serialized = KRD.Common.GenericXmlSerialization.Serialize(toCopy);
return KRD.Common.GenericXmlSerialization.Deserialize<T>(serialized);
}
public object Replace(object original, object target, object owner)
{
throw new NotImplementedException();
}
public object Assemble(object cached, object owner)
{
var str = cached as string;
if (string.IsNullOrWhiteSpace(str) == false)
{
return null;
}
return KRD.Common.GenericXmlSerialization.Deserialize<T>(str);
}
public object Disassemble(object value)
{
var toCache = value as T;
if (toCache != null)
{
return KRD.Common.GenericXmlSerialization.Serialize(toCache);
}
return null;
}
public SqlType[] SqlTypes
{
get
{
return new SqlType[] { new SqlXmlType() };
}
}
public Type ReturnedType
{
get { return typeof(XmlDocument); }
}
public bool IsMutable
{
get { return true; }
}
}
public class SqlXmlType : SqlType
{
public SqlXmlType()
: base(DbType.Xml)
{
}
}
Usage with FluentNHibernate:
public class MainObject
{
public int Id { get; set; }
public ObjectAsXml Data { get; set; }
}
public class ObjectAsXml
{
public string Name { get; set; }
public int Date { get; set; }
public ObjectAsXml OtherObject { get; set; }
}
private class MainObjectMap : ClassMap<MainObject>
{
public MainObjectMap()
{
Id(id => id.Id);
Map(m => m.Data).CustomType<XmlUserType<ObjectAsXml>>().Nullable();
}
}
Maybe it will help somebody.