using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using System.ComponentModel;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public class FixIConvertibleConverter : JsonConverter
readonly IContractResolver resolver;
public FixIConvertibleConverter() : this(JsonSerializer.CreateDefault().ContractResolver) { }
public FixIConvertibleConverter(IContractResolver resolver)
throw new ArgumentNullException();
this.resolver = resolver;
public override bool CanConvert(Type objectType)
var type = Nullable.GetUnderlyingType(objectType) ?? objectType;
if (!typeof(IConvertible).IsAssignableFrom(type))
var contract = resolver.ResolveContract(type) as JsonStringContract;
if (contract.Converter != null)
var converter = TypeDescriptor.GetConverter(type);
if (!converter.CanConvertTo(typeof(string)))
if (!converter.CanConvertFrom(typeof(string)))
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
var type = Nullable.GetUnderlyingType(objectType) ?? objectType;
if (reader.TokenType != JsonToken.String)
throw new JsonSerializationException();
var s = (string)reader.Value;
return TypeDescriptor.GetConverter(type).ConvertFromInvariantString(s);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
writer.WriteValue(TypeDescriptor.GetConverter(value.GetType()).ConvertToInvariantString(value));
public static partial class JsonExtensions
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None)
while (reader.TokenType == JsonToken.Comment)
public static JsonReader ReadAndAssert(this JsonReader reader)
throw new ArgumentNullException();
throw new JsonReaderException("Unexpected end of JSON stream.");
public class FriendlyUrlTypeConverter : TypeConverter
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
return value is string sValue ? new FriendlyUrl(sValue) : base.ConvertFrom(context, culture, value);
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
return destinationType == typeof(string) ? value.ToString() : base.ConvertTo(context, culture, value, destinationType);
public class FriendlyUrlValidationException : Exception
public FriendlyUrlValidationException()
public FriendlyUrlValidationException(string message) : base(message)
public FriendlyUrlValidationException(string message, Exception inner) : base(message, inner)
protected FriendlyUrlValidationException(
StreamingContext context) : base(info, context)
[TypeConverter(typeof(FriendlyUrlTypeConverter))]
public class FriendlyUrl : IEquatable<FriendlyUrl>, IConvertible
_friendlyUrl = string.Empty;
public FriendlyUrl(string value)
public static implicit operator FriendlyUrl(string friendlyUrlValue)
return new FriendlyUrl { _friendlyUrl = friendlyUrlValue };
public static implicit operator string(FriendlyUrl furl)
return furl._friendlyUrl;
public override string ToString()
private string _friendlyUrl;
public bool Equals(FriendlyUrl other)
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(_friendlyUrl, other._friendlyUrl, StringComparison.InvariantCultureIgnoreCase);
public override bool Equals(object obj)
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj is string stringValue) return string.Equals(_friendlyUrl, stringValue, StringComparison.InvariantCultureIgnoreCase);
if (obj.GetType() != GetType()) return false;
return Equals((FriendlyUrl) obj);
public override int GetHashCode()
return _friendlyUrl != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(_friendlyUrl) : 0;
public static bool operator ==(FriendlyUrl left, FriendlyUrl right)
return Equals(left, right);
public static bool operator !=(FriendlyUrl left, FriendlyUrl right)
return !Equals(left, right);
TypeCode IConvertible.GetTypeCode()
return _friendlyUrl.GetTypeCode();
bool IConvertible.ToBoolean(IFormatProvider provider)
return ((IConvertible) _friendlyUrl).ToBoolean(provider);
byte IConvertible.ToByte(IFormatProvider provider)
return ((IConvertible) _friendlyUrl).ToByte(provider);
char IConvertible.ToChar(IFormatProvider provider)
return ((IConvertible) _friendlyUrl).ToChar(provider);
DateTime IConvertible.ToDateTime(IFormatProvider provider)
return ((IConvertible) _friendlyUrl).ToDateTime(provider);
decimal IConvertible.ToDecimal(IFormatProvider provider)
return ((IConvertible) _friendlyUrl).ToDecimal(provider);
double IConvertible.ToDouble(IFormatProvider provider)
return ((IConvertible) _friendlyUrl).ToDouble(provider);
short IConvertible.ToInt16(IFormatProvider provider)
return ((IConvertible) _friendlyUrl).ToInt16(provider);
int IConvertible.ToInt32(IFormatProvider provider)
return ((IConvertible) _friendlyUrl).ToInt32(provider);
long IConvertible.ToInt64(IFormatProvider provider)
return ((IConvertible) _friendlyUrl).ToInt64(provider);
sbyte IConvertible.ToSByte(IFormatProvider provider)
return ((IConvertible) _friendlyUrl).ToSByte(provider);
float IConvertible.ToSingle(IFormatProvider provider)
return ((IConvertible) _friendlyUrl).ToSingle(provider);
string IConvertible.ToString(IFormatProvider provider)
return _friendlyUrl.ToString(provider);
object IConvertible.ToType(Type conversionType, IFormatProvider provider)
if (conversionType == typeof(FriendlyUrl))
return ((IConvertible) _friendlyUrl).ToType(conversionType, provider);
ushort IConvertible.ToUInt16(IFormatProvider provider)
return ((IConvertible) _friendlyUrl).ToUInt16(provider);
uint IConvertible.ToUInt32(IFormatProvider provider)
return ((IConvertible) _friendlyUrl).ToUInt32(provider);
ulong IConvertible.ToUInt64(IFormatProvider provider)
return ((IConvertible) _friendlyUrl).ToUInt64(provider);
public static void Test()
Console.WriteLine(TypeDescriptor.GetConverter(typeof(FriendlyUrl)).CanConvertTo(typeof(string)));
var settings = new JsonSerializerSettings
Converters = { new FixIConvertibleConverter() },
FriendlyUrl friendlyUrl = "some-friendly-url-1";
string friendlyUrlJson = JsonConvert.SerializeObject(friendlyUrl, settings);
Assert.AreEqual($"\"{friendlyUrl}\"", friendlyUrlJson);
Console.WriteLine("Json = {0}", friendlyUrlJson);
FriendlyUrl deserialized = JsonConvert.DeserializeObject<FriendlyUrl>(friendlyUrlJson, settings);
Assert.AreEqual(friendlyUrl, deserialized);
Console.WriteLine("Deserialized {0} = {1}", deserialized.GetType().Name, deserialized);
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("{0} version: {1}", typeof(JsonSerializer).Assembly.GetName().Name, typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");
public static string GetNetCoreVersion()
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.Location.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];