I am having a bit of a problem using AutoMapper with some Configuration Elements.
I have the following classes:
public class SocialLinkSettingConfiguration : ConfigurationSection
{
[ConfigurationProperty("name", IsRequired = true, IsKey = true)]
public string Name
{
get { return this["name"] as string; }
set { this["name"] = value; }
}
[ConfigurationProperty("description", IsRequired = true)]
public string Description
{
get { return this["description"] as string; }
set { this["description"] = value; }
}
[ConfigurationProperty("url", IsRequired = true)]
public string Url
{
get { return this["url"] as string; }
set { this["url"] = value; }
}
[ConfigurationProperty("fontAwesomeClass", IsRequired = false)]
public string FontAwesomeClass
{
get { return this["fontAwesomeClass"] as string; }
set { this["fontAwesomeClass"] = value; }
}
[ConfigurationProperty("imageUrl", IsRequired = false)]
public string ImageUrl
{
get { return this["imageUrl"] as string; }
set { this["imageUrl"] = value; }
}
}
public class SocialLinkSettingConfigurationCollection : ConfigurationElementCollection
{
public SocialLinkSettingConfiguration this[int index]
{
get { return (SocialLinkSettingConfiguration)BaseGet(index); }
set
{
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
public void Add(SocialLinkSettingConfiguration serviceConfig)
{
BaseAdd(serviceConfig);
}
public void Clear()
{
BaseClear();
}
protected override ConfigurationElement CreateNewElement()
{
return new SocialLinkSettingConfiguration();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((SocialLinkSettingConfiguration)element).Name;
}
public void Remove(SocialLinkSettingConfiguration serviceConfig)
{
BaseRemove(serviceConfig.Name);
}
public void RemoveAt(int index)
{
BaseRemoveAt(index);
}
public void Remove(string name)
{
BaseRemove(name);
}
}
public class SocialLinkSetting
{
public string Name { get; set; }
public string Description { get; set; }
public string Url { get; set; }
public string FontAwesomeClass { get; set; }
public string ImageUrl { get; set; }
}
I can create a mapping between SocialLinkSetting and SocialLinkSettingConfiguration just fine.
However, when I am trying to convert a list of SocialLinkSetting to SocialLinkSettingConfigurationCollection, I fail every time.
What I am trying to do is take a list of Social Links, and convert it to a single SocialLinkSettingConfigurationCollection object that has all of the SocialLinkSetting inside and converted to SocialLinkSettingConfiguration.
I can't seem to do this. Each time, it doesn't know how to convert List to the SocialLinkSettingConfigurationCollection, then add each of the items.
Any help would be greatly appreciated.
Thanks,
Ben
You can do something like this:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<SocialLinkSetting, SocialLinkSettingConfiguration>();
cfg.CreateMap<List<SocialLinkSetting>, SocialLinkSettingConfigurationCollection>()
.AfterMap((source, dest, resolutionContext) =>
{
dest.Clear();
source.ForEach(i => dest.Add(resolutionContext.Mapper.Map<SocialLinkSettingConfiguration>(i)));
});
});
I've created a wrapper in my data access for joins in OrmLite.
I'm now getting the exception:
System.Exception : Expression should have only one column
All of my entities have a base class of BaseEntity.
JoinType is just a facade to contain the column, selection and where of a join.
My wrapper is as follows:
public IEnumerable<TResultEntity> Join<TResultEntity>(IList<JoinType<BaseEntity, BaseEntity>> joins)
{
var result = new List<TResultEntity>();
if (joins != null && joins.Any())
{
var joinBuilder = new JoinSqlBuilder<T, BaseEntity>();
foreach (var join in joins)
{
joinBuilder = joinBuilder.Join(join.LeftColumn, join.RightColumn, join.LeftSelection, join.RightSelection, join.LeftWhere, join.RightWhere);
}
var connection = this.DataConnection;
using (connection)
{
var joinSql = joinBuilder.SelectDistinct().ToSql();
result = connection.SqlList<TResultEntity>(joinSql);
}
}
return result;
}
Doing the same thing, without the list seems to work:
public IEnumerable<TResultEntity> Join<TLeftTable1, TRightTable1, TLeftTable2, TRightTable2, TResultEntity>(
JoinType<TLeftTable1, TRightTable1> join1,
JoinType<TLeftTable2, TRightTable2> join2)
where TLeftTable1 : BaseEntity
where TRightTable1 : BaseEntity
where TLeftTable2 : BaseEntity
where TRightTable2 : BaseEntity
EDIT - I'm testing using the below call:
// Act
var join1 = new JoinType<AnswerEntity, UserSurveyStateEntity>(
l => l.OwnerId,
r => r.UserId,
x => new { UserId = x.OwnerId, x.QuestionId, AnswerId = x.Id, x.AnswerValue });
var join2 = new JoinType<SurveyEntity, UserSurveyStateEntity>(
l => l.Id,
r => r.SurveyInstanceId,
x => new { SurveyId = x.Id, SurveyName = x.Name, x.StatusValue },
null,
null,
x => x.StatusValue == (int)UserSurveyStatus.Complete);
var joins = new List<JoinType<BaseEntity, BaseEntity>>();
joins.Add(join1.As<JoinType<BaseEntity, BaseEntity>>());
joins.Add(join2.As<JoinType<BaseEntity, BaseEntity>>());
var result = dataAccess.Join<AnswerEntity>(joins).ToList();
EDIT - Now seeing the use case, the error is related to casting to the base type and the builder storing more than one column selector for the concrete BaseEntity. Consider adding an abstract JoinType class, and modifying the JoinType class so it will apply the join for the builder.
For example:
public class Entity
{
public string Id { get; set; }
}
public class Foo
: Entity
{
public string Value { get; set; }
}
public class Bar
: Entity
{
public string FooId { get; set; }
public string Value { get; set; }
}
public abstract class JoinType
{
public abstract JoinSqlBuilder<TNew, TBase> ApplyJoin<TNew, TBase>(
JoinSqlBuilder<TNew, TBase> bldr);
}
public class JoinType<TSource, TTarget>
: JoinType
{
private Expression<Func<TSource, object>> _sourceColumn;
private Expression<Func<TTarget, object>> _destinationColumn;
private Expression<Func<TSource, object>> _sourceTableColumnSelection;
private Expression<Func<TTarget, object>> _destinationTableColumnSelection;
private Expression<Func<TSource, bool>> _sourceWhere;
private Expression<Func<TTarget, bool>> _destinationWhere;
public JoinType(Expression<Func<TSource, object>> sourceColumn,
Expression<Func<TTarget, object>> destinationColumn,
Expression<Func<TSource, object>>
sourceTableColumnSelection = null,
Expression<Func<TTarget, object>>
destinationTableColumnSelection = null,
Expression<Func<TSource, bool>> sourceWhere = null,
Expression<Func<TTarget, bool>> destinationWhere =
null)
{
this._sourceColumn = sourceColumn;
this._destinationColumn = destinationColumn;
this._sourceTableColumnSelection = sourceTableColumnSelection;
this._destinationTableColumnSelection =
destinationTableColumnSelection;
this._sourceWhere = sourceWhere;
this._destinationWhere = destinationWhere;
}
public override JoinSqlBuilder<TNew, TBase> ApplyJoin<TNew, TBase>(
JoinSqlBuilder<TNew, TBase> bldr)
{
bldr.Join(_sourceColumn,
_destinationColumn,
_sourceTableColumnSelection,
_destinationTableColumnSelection,
_sourceWhere,
_destinationWhere);
return bldr;
}
}
public class FooBar
{
[References(typeof(Foo))]
public string FooId { get; set; }
[References(typeof(Bar))]
public string BarId { get; set; }
[References(typeof(Foo))]
public string FooValue { get; set; }
[References(typeof(Bar))]
public string BarValue { get; set; }
}
/*
This join accomplishes the same thing, but just returns the SQL as a string.
*/
public string Join<TResultEntity,TBase>(IList<JoinType>joins)
{
var result = new List<TResultEntity>();
if (joins != null && joins.Any())
{
var joinBuilder = new JoinSqlBuilder<TResultEntity, TBase>();
foreach (var joinType in joins)
{
//call the apply join, and the join type will know the valid types
joinBuilder = joinType.ApplyJoin(joinBuilder);
}
return joinBuilder.SelectDistinct().ToSql();
}
return null;
}
[TestMethod]
public void TestMethod1()
{
OrmLiteConfig.DialectProvider = SqlServerDialect.Provider;
var joins = new List<JoinType>();
var jointype1 = new JoinType<Bar, FooBar>(
bar => bar.Id,
bar => bar.BarId,
bar => new { BarId = bar.Id, BarValue = bar.Value }
);
joins.Add(jointype1);
var joinType2 = new JoinType<Foo, FooBar>(
foo => foo.Id,
bar => bar.FooId,
foo => new { FooId = foo.Id, FooValue = foo.Value}
);
joins.Add(joinType2);
var str = Join<FooBar, Bar>(joins);
}
Old Answer - still relevant to the error
This error is caused by your selector join.LeftColumn or your join.RightColumn containing two selectors. Make sure they only contain a single one.
I was able to reproduce the error with the following test:
public class Entity
{
public string Id { get; set; }
}
public class Foo
: Entity
{
public string Value { get; set; }
}
public class Bar
: Entity
{
public string FooId { get; set; }
public string Value { get; set; }
}
public class FooBar
{
[References(typeof(Foo))]
public string FooId { get; set; }
[References(typeof(Bar))]
public string BarId { get; set; }
[References(typeof(Foo))]
public string FooValue { get; set; }
[References(typeof(Bar))]
public string BarValue { get; set; }
}
[TestMethod]
public void TestMethod1()
{
OrmLiteConfig.DialectProvider = SqlServerDialect.Provider;
var bldr = new JoinSqlBuilder<FooBar,Bar>();
bldr = bldr.Join<FooBar, Bar>(
bar => bar.BarId,
bar => new { Id1 = bar.Id, Id2 = bar.Id},//<-- this should only contain a single member
bar => new { BarId =bar.BarId },
bar => new { BarId = bar.Id, BarValue = bar.Value},
bar => bar.BarId != null,
bar => bar.Id != null
);
var str = bldr.SelectDistinct().ToSql();
}
Suppose I have the following class hierarchy:
public abstract class Organization
{
/* properties related to all organizations */
}
public sealed class Company : Organization
{
/* properties related to companies */
}
public sealed class NonProfitOrganization : Organization
{
/* properties related to non profit organizations */
}
Is it possible to have json.net use property (say "type" or "discriminator") to determine which type the object when it deserializes the organization? For example, the following should deserialize an instance of Company.
{
"type": "company"
/* other properties related to companies */
}
And the following should deserialize an instance of NonProfitOrganization.
{
"type": "non-profit"
/* other properties related to non profit */
}
When I call the following:
Organization organization = JsonConvert.DeserializeObject<Organization>(payload);
where payload is the above JSON snippets. I had a look at setting the "TypeNameHandling" on properties or classes but it serializes the whole .NET type, which isn't "portable" between the client and server when the classes are defined in different namespaces and assemblies.
I'd rather define the type is a neutral manner which clients written in any language can use to determine the actual type of the object type being serialized.
In case you are still looking, here is an example: http://james.newtonking.com/archive/2011/11/19/json-net-4-0-release-4-bug-fixes.aspx
This will allow you to create a table based mapping:
public class TypeNameSerializationBinder : SerializationBinder
{
public TypeNameSerializationBinder(Dictionary<Type, string> typeNames = null)
{
if (typeNames != null)
{
foreach (var typeName in typeNames)
{
Map(typeName.Key, typeName.Value);
}
}
}
readonly Dictionary<Type, string> typeToName = new Dictionary<Type, string>();
readonly Dictionary<string, Type> nameToType = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
public void Map(Type type, string name)
{
this.typeToName.Add(type, name);
this.nameToType.Add(name, type);
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
var name = typeToName.Get(serializedType);
if (name != null)
{
assemblyName = null;
typeName = name;
}
else
{
assemblyName = serializedType.Assembly.FullName;
typeName = serializedType.FullName;
}
}
public override Type BindToType(string assemblyName, string typeName)
{
if (assemblyName == null)
{
var type = this.nameToType.Get(typeName);
if (type != null)
{
return type;
}
}
return Type.GetType(string.Format("{0}, {1}", typeName, assemblyName), true);
}
}
The code has a slight defect in that if a type name mapping is attempted where the type is unique but the name is already used, the Map method will throw an exception after the type-to-name mapping is already added leaving the table in an inconsistent state.
To take eulerfx's answer further; I wanted to apply DisplayName attribute to a class and have that automatically become the type name used; to that end:
public class DisplayNameSerializationBinder : DefaultSerializationBinder
{
private Dictionary<string, Type> _nameToType;
private Dictionary<Type, string> _typeToName;
public DisplayNameSerializationBinder()
{
var customDisplayNameTypes =
this.GetType()
.Assembly
//concat with references if desired
.GetTypes()
.Where(x => x
.GetCustomAttributes(false)
.Any(y => y is DisplayNameAttribute));
_nameToType = customDisplayNameTypes.ToDictionary(
t => t.GetCustomAttributes(false).OfType<DisplayNameAttribute>().First().DisplayName,
t => t);
_typeToName = _nameToType.ToDictionary(
t => t.Value,
t => t.Key);
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
if (false == _typeToName.ContainsKey(serializedType))
{
base.BindToName(serializedType, out assemblyName, out typeName);
return;
}
var name = _typeToName[serializedType];
assemblyName = null;
typeName = name;
}
public override Type BindToType(string assemblyName, string typeName)
{
if (_nameToType.ContainsKey(typeName))
return _nameToType[typeName];
return base.BindToType(assemblyName, typeName);
}
}
and usage example:
public class Parameter
{
public string Name { get; set; }
};
[DisplayName("bool")]
public class BooleanParameter : Parameter
{
}
[DisplayName("string")]
public class StringParameter : Parameter
{
public int MinLength { get; set; }
public int MaxLength { get; set; }
}
[DisplayName("number")]
public class NumberParameter : Parameter
{
public double Min { get; set; }
public double Max { get; set; }
public string Unit { get; set; }
}
[DisplayName("enum")]
public class EnumParameter : Parameter
{
public string[] Values { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
var parameters = new Parameter[]
{
new BooleanParameter() {Name = "alive"},
new StringParameter() {Name = "name", MinLength = 0, MaxLength = 10},
new NumberParameter() {Name = "age", Min = 0, Max = 120},
new EnumParameter() {Name = "status", Values = new[] {"Single", "Married"}}
};
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
Binder = new DisplayNameSerializationBinder(),
TypeNameHandling = TypeNameHandling.Auto,
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore,
Formatting = Formatting.Indented,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var json = JsonConvert.SerializeObject(parameters);
var loadedParams = JsonConvert.DeserializeObject<Parameter[]>(json);
Console.WriteLine(JsonConvert.SerializeObject(loadedParams));
}
}
output:
[
{
"$type": "bool",
"name": "alive"
},
{
"$type": "string",
"maxLength": 10,
"name": "name"
},
{
"$type": "number",
"max": 120.0,
"name": "age"
},
{
"$type": "enum",
"values": [
"Single",
"Married"
],
"name": "status"
}
]
I've written purely declarative solution with ability to specify custom discriminator field, and provide scoped name handling per base class (as opposed to usecure global JsonSerializationSettings, especially on different Web-Api when we do not have ability to specify custom JsonSerializationSettings).
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Reflection;
using System.Linq;
using System.Collections.Generic;
// Discriminated Json Converter (JsonSubtypes) implementation for .NET
//
// MIT License
//
// Copyright (c) 2016 Anatoly Ressin
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
////////////////////// USAGE ////////////////////////////////////////////////////////////////////////////////
[JsonConverter(typeof(JsonSubtypes))] // Discriminated base class SHOULD NOT be abstract
public class ShapeBase {
[JsonTag, JsonProperty("#type")] // it SHOULD contain a property marked with [JsonTag]
public string Type {get;set;} // only one [JsonTag] annotation allowed per discriminated class
// it COULD contain other properties, however this is NOT RECOMMENDED
// Rationale: instances of this class will be created at deserialization
// only for tag sniffing, and then thrown away.
}
public abstract class Shape: ShapeBase { // If you want abstract parent - extend the root
public abstract double GetArea(); // with needed abstract stuff, then use this class everywhere (see DEMO below)
}
[JsonSubtype("circle")] // Every final class-case SHOULD be marked with [JsonSubtype(tagValue)]
public class Circle: Shape { // Two disctinct variant classes MUST have distinct tagValues
[JsonProperty("super-radius")] // You CAN use any Json-related annotation as well
public double Radius { get; set; }
public override double GetArea() {
return Radius * Radius * Math.PI;
}
}
[JsonSubtype("rectangle")]
public class Rectangle: Shape {
public double Height { get; set; }
public double Width { get; set; }
public override double GetArea() {
return Width * Height;
}
}
[JsonSubtype("group")]
public class Group: Shape {
[JsonProperty("shapes")]
public List<Shape> Items { get; set; }
public override double GetArea() {
return Items.Select(item => item.GetArea()).Sum();
}
}
// Every final class-case SHOULD be registered with JsonSubtypes.register(typeof(YourConcreteClass))
// either manually or with auto-register capability:
// You can auto-register all classes marked with [JsonSubtype(tag)] in given Assembly
// using JsonSubtypes.autoRegister(yourAssembly)
////////////////// DEMO /////////////////////////////////////////////////////////////////////////////////
public class Program
{
public static void Main()
{
JsonSubtypes.autoRegister(Assembly.GetExecutingAssembly());
Shape original = new Group() {
Items = new List<Shape> {
new Circle() { Radius = 5 },
new Rectangle() { Height = 10, Width = 20 }
}
};
string str = JsonConvert.SerializeObject(original);
Console.WriteLine(str);
var copy = JsonConvert.DeserializeObject(str,typeof(Shape)) as Shape;
// Note: we can deserialize object using any class from the hierarchy.
// Under the hood, anyway, it will be deserialized using the top-most
// base class annotated with [JsonConverter(typeof(JsonSubtypes))].
// Thus, only soft-casts ("as"-style) are safe here.
Console.WriteLine("original.area = {0}, copy.area = {1}", original.GetArea(), copy.GetArea());
}
}
//////////////////////// IMPLEMENTATION //////////////////////////////////////////////////////////////////
public class JsonSubtypeClashException: Exception {
public string TagValue { get; private set;}
public Type RootType { get; private set; }
public Type OldType { get; private set; }
public Type NewType { get; private set; }
public JsonSubtypeClashException(Type rootType, string tagValue, Type oldType, Type newType): base(
String.Format(
"JsonSubtype Clash for {0}[tag={1}]: oldType = {2}, newType = {3}",
rootType.FullName,
tagValue,
oldType.FullName,
newType.FullName
)
) {
TagValue = tagValue;
RootType = rootType;
OldType = oldType;
NewType = newType;
}
}
public class JsonSubtypeNoRootException: Exception {
public Type SubType { get; private set; }
public JsonSubtypeNoRootException(Type subType): base(
String.Format(
"{0} should be inherited from the class with the [JsonConverter(typeof(JsonSubtypes))] attribute",
subType.FullName
)
) {
SubType = subType;
}
}
public class JsonSubtypeNoTagException: Exception {
public Type SubType { get; private set; }
public JsonSubtypeNoTagException(Type subType): base(
String.Format(
#"{0} should have [JsonSubtype(""..."")] attribute",
subType.FullName
)
) {
SubType = subType;
}
}
public class JsonSubtypeNotRegisteredException: Exception {
public Type Root { get; private set; }
public string TagValue { get; private set; }
public JsonSubtypeNotRegisteredException(Type root, string tagValue): base(
String.Format(
#"Unknown tag={1} for class {0}",
root.FullName,
tagValue
)
) {
Root = root;
TagValue = tagValue;
}
}
[AttributeUsage(AttributeTargets.Class)]
public class JsonSubtypeAttribute: Attribute {
private string tagValue;
public JsonSubtypeAttribute(string tagValue) {
this.tagValue = tagValue;
}
public string TagValue {
get {
return tagValue;
}
}
}
public static class JsonSubtypesExtension {
public static bool TryGetAttribute<T>(this Type t, out T attribute) where T: Attribute {
attribute = t.GetCustomAttributes(typeof(T), false).Cast<T>().FirstOrDefault();
return attribute != null;
}
private static Dictionary<Type, PropertyInfo> tagProperties = new Dictionary<Type, PropertyInfo>();
public static bool TryGetTagProperty(this Type t, out PropertyInfo tagProperty) {
if (!tagProperties.TryGetValue(t, out tagProperty)) {
JsonConverterAttribute conv;
if (t.TryGetAttribute(out conv) && conv.ConverterType == typeof(JsonSubtypes)) {
var props = (from prop in t.GetProperties() where prop.GetCustomAttribute(typeof(JsonTagAttribute)) != null select prop).ToArray();
if (props.Length == 0) throw new Exception("No tag");
if (props.Length > 1) throw new Exception("Multiple tags");
tagProperty = props[0];
} else {
tagProperty = null;
}
tagProperties[t] = tagProperty;
}
return tagProperty != null;
}
public static bool TryGetTagValue(this Type t, out string tagValue) {
JsonSubtypeAttribute subtype;
if (t.TryGetAttribute(out subtype)) {
tagValue = subtype.TagValue;
return true;
} else {
tagValue = null;
return false;
}
}
public static bool TryGetJsonRoot(this Type t, out Type root, out PropertyInfo tagProperty) {
root = t;
do {
if (root.TryGetTagProperty(out tagProperty)) {
return true;
}
root = root.BaseType;
} while (t != null);
return false;
}
}
public class JsonTagAttribute: Attribute {
}
public class JsonTagInfo {
public PropertyInfo Property { get; set; }
public string Value { get; set; }
}
public class JsonRootInfo {
public PropertyInfo Property { get; set; }
public Type Root { get; set; }
}
public abstract class DefaultJsonConverter: JsonConverter {
[ThreadStatic]
private static bool silentWrite;
[ThreadStatic]
private static bool silentRead;
public sealed override bool CanWrite {
get {
var canWrite = !silentWrite;
silentWrite = false;
return canWrite;
}
}
public sealed override bool CanRead {
get {
var canRead = !silentRead;
silentRead = false;
return canRead;
}
}
protected void _WriteJson(JsonWriter writer, Object value, JsonSerializer serializer) {
silentWrite = true;
serializer.Serialize(writer, value);
}
protected Object _ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) {
silentRead = true;
return serializer.Deserialize(reader, objectType);
}
}
public class JsonSubtypes: DefaultJsonConverter {
private static Dictionary<Type, Dictionary<string, Type>> implementations = new Dictionary<Type, Dictionary<string, Type>>();
private static Dictionary<Type, JsonTagInfo> tags = new Dictionary<Type, JsonTagInfo>();
private static Dictionary<Type, JsonRootInfo> roots = new Dictionary<Type, JsonRootInfo>();
public static void register(Type newType) {
PropertyInfo tagProperty;
Type root;
if (newType.TryGetJsonRoot(out root, out tagProperty)) {
for(var t = newType; t != root; t = t.BaseType) {
roots[t] = new JsonRootInfo() {
Property = tagProperty,
Root = root
};
}
roots[root] = new JsonRootInfo() {
Property = tagProperty,
Root = root
};
Dictionary<string, Type> implementationMap;
if (!implementations.TryGetValue(root, out implementationMap)) {
implementationMap = new Dictionary<string, Type>();
implementations[root] = implementationMap;
}
JsonSubtypeAttribute attr;
if (!newType.TryGetAttribute(out attr)) {
throw new JsonSubtypeNoTagException(newType);
}
var tagValue = attr.TagValue;
Type oldType;
if (implementationMap.TryGetValue(tagValue, out oldType)) {
throw new JsonSubtypeClashException(root, tagValue, oldType, newType);
}
implementationMap[tagValue] = newType;
tags[newType] = new JsonTagInfo() {
Property = tagProperty,
Value = tagValue
};
} else {
throw new JsonSubtypeNoRootException(newType);
}
}
public static void autoRegister(Assembly assembly) {
foreach(var type in assembly.GetTypes().Where(type => type.GetCustomAttribute<JsonSubtypeAttribute>() != null)) {
register(type);
}
}
public override bool CanConvert(Type t) {
return true;
}
public static T EnsureTag<T>(T value) {
JsonTagInfo tagInfo;
if (tags.TryGetValue(value.GetType(), out tagInfo)) {
tagInfo.Property.SetValue(value, tagInfo.Value);
}
return value;
}
public override void WriteJson(JsonWriter writer, Object value, JsonSerializer serializer) {
_WriteJson(writer, EnsureTag(value), serializer);
}
public override Object ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) {
JsonTagInfo tagInfo;
if (tags.TryGetValue(objectType, out tagInfo)) {
return _ReadJson(reader, objectType, existingValue, serializer);
} else {
JsonRootInfo rootInfo;
if (roots.TryGetValue(objectType, out rootInfo)) {
JToken t = JToken.ReadFrom(reader);
var stub = _ReadJson(t.CreateReader(), rootInfo.Root, existingValue, serializer);
var tagValue = rootInfo.Property.GetValue(stub) as string;
var implementationMap = implementations[rootInfo.Root];
Type implementation;
if (implementationMap.TryGetValue(tagValue, out implementation)) {
return ReadJson(t.CreateReader(), implementation, null, serializer);
} else {
throw new JsonSubtypeNotRegisteredException(rootInfo.Root, tagValue);
}
} else {
return _ReadJson(reader, objectType, existingValue, serializer);
}
}
}
public static T Deserialize<T>(string s) where T: class {
return JsonConvert.DeserializeObject(s, typeof(T)) as T;
}
public static string Serialize<T>(T value) where T: class {
return JsonConvert.SerializeObject(value);
}
}
output:
{"shapes":[{"super-radius":5.0,"#type":"circle"},{"Height":10.0,"Width":20.0,"#type":"rectangle"}],"#type":"group"}
original.area = 278.539816339745, copy.area = 278.539816339745
You can grab it here:
https://dotnetfiddle.net/ELcvnk
With another JsonSubtypes converter implementation.
Usage:
[JsonConverter(typeof(JsonSubtypes), "Sound")]
[JsonSubtypes.KnownSubType(typeof(Dog), "Bark")]
[JsonSubtypes.KnownSubType(typeof(Cat), "Meow")]
public class Animal
{
public virtual string Sound { get; }
public string Color { get; set; }
}
public class Dog : Animal
{
public override string Sound { get; } = "Bark";
public string Breed { get; set; }
}
public class Cat : Animal
{
public override string Sound { get; } = "Meow";
public bool Declawed { get; set; }
}
[TestMethod]
public void Demo()
{
var input = #"{""Sound"":""Bark"",""Breed"":""Jack Russell Terrier""}"
var animal = JsonConvert.DeserializeObject<Animal>(input);
Assert.AreEqual("Jack Russell Terrier", (animal as Dog)?.Breed);
}
the converter implementation can be directly downloaded from the repository: JsonSubtypes.cs and is also availble as a nuget package
Use this JsonKnownTypes, it's very similar way to use, add couple of attribute:
[JsonConverter(typeof(JsonKnownTypeConverter<Organization>))]
[JsonDiscriminator(Name = "discriminator")]
[JsonKnownType(typeof(Company), "company")]
[JsonKnownType(typeof(NonProfitOrganization), "non-profit")]
public abstract class Organization
{
/* properties related to all organizations */
}
public sealed class Company : Organization
{
/* properties related to companies */
}
public sealed class NonProfitOrganization : Organization
{
/* properties related to non profit organizations */
}
And serialize:
var json = JsonConvert.SerializeObject(youObject)
Output json:
{..., "discriminator":"non-profit"} //if object was NonProfitOrganization
Deserialization:
var organization = JsonConvert.DeserializeObject<Organization>(payload);
I am getting the token from acs as describe in this post Validating a SWT Token REST WCF Service
But i was not able to extract the ACS token.
Could you please help me in that.
Actually the class the extract the acs token "JsonNotifyRequestSecurityTokenResponse.FromJson"
I was not able to get that as the class as the link provided is not working.
I think you posted this question as an answer at my question some hours ago and then deleted it. So i looked up my old project and found the code you need:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IdentityModel.Policy;
using System.IdentityModel.Tokens;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Web;
using System.Xml.Linq;
using Thinktecture.IdentityModel.Extensions;
namespace Security {
[DataContract]
public class JsonNotifyRequestSecurityTokenResponse {
[DataMember(Name = "appliesTo", Order = 1)]
public string AppliesTo { get; set; }
[DataMember(Name = "context", Order = 2)]
public string Context { get; set; }
[DataMember(Name = "created", Order = 3)]
public string Created { get; set; }
[DataMember(Name = "expires", Order = 4)]
public string Expires { get; set; }
[DataMember(Name = "securityToken", Order = 5)]
public string SecurityTokenString { get; set; }
[DataMember(Name = "tokenType", Order = 6)]
public string TokenType { get; set; }
public DateTime ValidFrom { get; set; }
public DateTime ValidTo { get; set; }
public GenericXmlSecurityToken SecurityToken { get; set; }
public static JsonNotifyRequestSecurityTokenResponse FromJson(string jsonString) {
JsonNotifyRequestSecurityTokenResponse rstr;
var memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(jsonString));
var serializer = new DataContractJsonSerializer(typeof(JsonNotifyRequestSecurityTokenResponse));
rstr = serializer.ReadObject(memoryStream) as JsonNotifyRequestSecurityTokenResponse;
memoryStream.Close();
ParseValues(rstr);
return rstr;
}
private static void ParseValues(JsonNotifyRequestSecurityTokenResponse rstr) {
rstr.ValidFrom = ulong.Parse(rstr.Created).ToDateTimeFromEpoch();
rstr.ValidTo = ulong.Parse(rstr.Expires).ToDateTimeFromEpoch();
rstr.SecurityTokenString = HttpUtility.HtmlDecode(rstr.SecurityTokenString);
var xml = XElement.Parse(rstr.SecurityTokenString);
string idAttribute = "";
string tokenType = "";
switch (rstr.TokenType) {
case SecurityTokenTypes.Saml11:
idAttribute = "AssertionID";
tokenType = SecurityTokenTypes.Saml11;
break;
case SecurityTokenTypes.Saml2:
idAttribute = "ID";
tokenType = SecurityTokenTypes.Saml2;
break;
case SecurityTokenTypes.SWT:
idAttribute = "Id";
tokenType = SecurityTokenTypes.SWT;
break;
}
if (tokenType == SecurityTokenTypes.Saml11 || tokenType == SecurityTokenTypes.Saml2) {
var tokenId = xml.Attribute(idAttribute);
var xmlElement = xml.ToXmlElement();
SecurityKeyIdentifierClause clause = null;
if (tokenId != null) {
clause = new SamlAssertionKeyIdentifierClause(tokenId.Value);
}
rstr.SecurityToken = new GenericXmlSecurityToken(
xmlElement,
null,
rstr.ValidFrom,
rstr.ValidTo,
clause,
clause,
new ReadOnlyCollection<IAuthorizationPolicy>(new List<IAuthorizationPolicy>()));
}
}
private class SecurityTokenTypes {
public const string Saml2 = "urn:oasis:names:tc:SAML:2.0:assertion";
public const string Saml11 = "urn:oasis:names:tc:SAML:1.0:assertion";
public const string SWT = "http://schemas.xmlsoap.org/ws/2009/11/swt-token-profile-1.0";
}
}
}
You should get the Thinktecture.IdentityModel.Extensions assembly from: http://identitymodel.codeplex.com/
I hope this helps.
EDIT - SWT token handling
I don't know if it helps, but I've found a SWTModule which should handle a SWT token.
public class SWTModule : IHttpModule {
void IHttpModule.Dispose() {
}
void IHttpModule.Init(HttpApplication context) {
context.BeginRequest += new EventHandler(context_BeginRequest);
}
void context_BeginRequest(object sender, EventArgs e) {
//HANDLE SWT TOKEN VALIDATION
// get the authorization header
string headerValue = HttpContext.Current.Request.Headers.Get("Authorization");
// check that a value is there
if (string.IsNullOrEmpty(headerValue)) {
throw new ApplicationException("unauthorized");
}
// check that it starts with 'WRAP'
if (!headerValue.StartsWith("WRAP ")) {
throw new ApplicationException("unauthorized");
}
string[] nameValuePair = headerValue.Substring("WRAP ".Length).Split(new char[] { '=' }, 2);
if (nameValuePair.Length != 2 ||
nameValuePair[0] != "access_token" ||
!nameValuePair[1].StartsWith("\"") ||
!nameValuePair[1].EndsWith("\"")) {
throw new ApplicationException("unauthorized");
}
// trim off the leading and trailing double-quotes
string token = nameValuePair[1].Substring(1, nameValuePair[1].Length - 2);
// create a token validator
TokenValidator validator = new TokenValidator(
this.acsHostName,
this.serviceNamespace,
this.trustedAudience,
this.trustedTokenPolicyKey);
// validate the token
if (!validator.Validate(token)) {
throw new ApplicationException("unauthorized");
}
}
}
And you also need the TokenValidator:
public class TokenValidator {
private string issuerLabel = "Issuer";
private string expiresLabel = "ExpiresOn";
private string audienceLabel = "Audience";
private string hmacSHA256Label = "HMACSHA256";
private string acsHostName;
private string trustedSigningKey;
private string trustedTokenIssuer;
private string trustedAudienceValue;
public TokenValidator(string acsHostName, string serviceNamespace, string trustedAudienceValue, string trustedSigningKey) {
this.trustedSigningKey = trustedSigningKey;
this.trustedTokenIssuer = String.Format("https://{0}.{1}/",
serviceNamespace.ToLowerInvariant(),
acsHostName.ToLowerInvariant());
this.trustedAudienceValue = trustedAudienceValue;
}
public bool Validate(string token) {
if (!this.IsHMACValid(token, Convert.FromBase64String(this.trustedSigningKey))) {
return false;
}
if (this.IsExpired(token)) {
return false;
}
if (!this.IsIssuerTrusted(token)) {
return false;
}
if (!this.IsAudienceTrusted(token)) {
return false;
}
return true;
}
public Dictionary<string, string> GetNameValues(string token) {
if (string.IsNullOrEmpty(token)) {
throw new ArgumentException();
}
return
token
.Split('&')
.Aggregate(
new Dictionary<string, string>(),
(dict, rawNameValue) => {
if (rawNameValue == string.Empty) {
return dict;
}
string[] nameValue = rawNameValue.Split('=');
if (nameValue.Length != 2) {
throw new ArgumentException("Invalid formEncodedstring - contains a name/value pair missing an = character");
}
if (dict.ContainsKey(nameValue[0]) == true) {
throw new ArgumentException("Repeated name/value pair in form");
}
dict.Add(HttpUtility.UrlDecode(nameValue[0]), HttpUtility.UrlDecode(nameValue[1]));
return dict;
});
}
private static ulong GenerateTimeStamp() {
// Default implementation of epoch time
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToUInt64(ts.TotalSeconds);
}
private bool IsAudienceTrusted(string token) {
Dictionary<string, string> tokenValues = this.GetNameValues(token);
string audienceValue;
tokenValues.TryGetValue(this.audienceLabel, out audienceValue);
if (!string.IsNullOrEmpty(audienceValue)) {
if (audienceValue.Equals(this.trustedAudienceValue, StringComparison.Ordinal)) {
return true;
}
}
return false;
}
private bool IsIssuerTrusted(string token) {
Dictionary<string, string> tokenValues = this.GetNameValues(token);
string issuerName;
tokenValues.TryGetValue(this.issuerLabel, out issuerName);
if (!string.IsNullOrEmpty(issuerName)) {
if (issuerName.Equals(this.trustedTokenIssuer)) {
return true;
}
}
return false;
}
private bool IsHMACValid(string swt, byte[] sha256HMACKey) {
string[] swtWithSignature = swt.Split(new string[] { "&" + this.hmacSHA256Label + "=" }, StringSplitOptions.None);
if ((swtWithSignature == null) || (swtWithSignature.Length != 2)) {
return false;
}
HMACSHA256 hmac = new HMACSHA256(sha256HMACKey);
byte[] locallyGeneratedSignatureInBytes = hmac.ComputeHash(Encoding.ASCII.GetBytes(swtWithSignature[0]));
string locallyGeneratedSignature = HttpUtility.UrlEncode(Convert.ToBase64String(locallyGeneratedSignatureInBytes));
return locallyGeneratedSignature == swtWithSignature[1];
}
private bool IsExpired(string swt) {
try {
Dictionary<string, string> nameValues = this.GetNameValues(swt);
string expiresOnValue = nameValues[this.expiresLabel];
ulong expiresOn = Convert.ToUInt64(expiresOnValue);
ulong currentTime = Convert.ToUInt64(GenerateTimeStamp());
if (currentTime > expiresOn) {
return true;
}
return false;
} catch (KeyNotFoundException) {
throw new ArgumentException();
}
}
}
I cannot seem to map a custom type using the latest autoMapper library in c#. I am using the sample from the sample app but added a custom type. Both classes are similar just the code names are different. I do want this to be a "GlobalTypeConverter" as it used a few times.
The error is at validation:
//Error: The following 1 properties on TestApp.Form1+Destination are not mapped:
mycode
Add a custom mapping expression, ignore, or rename the property on TestApp.Form1+Source.
public class Source
{
public string Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 { get; set; }
public standardCoding stdcode { get; set; }
}
public class Destination
{
public int Value1 { get; set; }
public DateTime Value2 { get; set; }
public Type Value3 { get; set; }
public myClass.code mycode { get; set; }
}
public class DateTimeTypeConverter : TypeConverter<string, DateTime>
{
protected override DateTime ConvertCore(string source)
{
return System.Convert.ToDateTime(source);
}
}
public class TypeTypeConverter : TypeConverter<string, Type>
{
protected override Type ConvertCore(string source)
{
Type type = Assembly.GetExecutingAssembly().GetType(source);
return type;
}
}
public class standardCodingConverter : TypeConverter<standardCoding, myClass.code>
{
protected override myClass.code ConvertCore(standardCoding source)
{
var result = new myClass.code();
result.CodingSystem = source.StandardCodingSystem;
result.Description = source.StandardCodeDescription;
result.value = source.StandardCode;
return result;
}
}
public void btnAutoMapperTest_Click(object sender, EventArgs e)
{
Mapper.CreateMap<string, int>().ConvertUsing(Convert.ToInt32);
Mapper.CreateMap<string, DateTime>().ConvertUsing(new DateTimeTypeConverter());
Mapper.CreateMap<string, Type>().ConvertUsing<TypeTypeConverter>();
Mapper.CreateMap<standardCoding, myClass.code>().ConvertUsing(new standardCodingConverter());
Mapper.CreateMap<Source, Destination>();
Mapper.AssertConfigurationIsValid();
var newcode = new standardCoding();
newcode.StandardCode = "123";
newcode.StandardCodeDescription = "my desc";
newcode.StandardCodingSystem = "CodeSys";
var source = new Source
{
Value1 = "5",
Value2 = "01/01/2000",
Value3 = "AutoMapperSamples.GlobalTypeConverters.GlobalTypeConverters+Destination",
stdcode = newcode
};
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "cds_dt")]
public partial class standardCoding
{
private string standardCodingSystemField;
private string standardCodeField;
private string standardCodeDescriptionField;
/// <remarks/>
public string StandardCodingSystem
{
get
{
return this.standardCodingSystemField;
}
set
{
this.standardCodingSystemField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(DataType = "token")]
public string StandardCode
{
get
{
return this.standardCodeField;
}
set
{
this.standardCodeField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(DataType = "token")]
public string StandardCodeDescription
{
get
{
return this.standardCodeDescriptionField;
}
set
{
this.standardCodeDescriptionField = value;
}
}
}
I figured it out by looking online (sample code "CustomValueResolvers.cs" did not work for me).
My one incorrect line was:
...
.ForMember(src => src.DiagnosisCode, opt => opt.ResolveUsing() .FromMember(r => r.DiagnosisProcedureCode))