How to serialize a Guid with C# without creating an object? - c#

I want my Guids to be serialized into a short form of Guid (as seen here). For full compatibility, I want the ShortGuid class to serialize into the short form, and not the long form.
I've tried using custom serialization, but I can't seem to make the whole object serialize into the short string, only into an object which contains the string. This is what I've tried:
[Serializable]
public class ShortGuid : ISerializable
{
public Guid Guid { get; }
protected ShortGuid(SerializationInfo info, StreamingContext context)
{
Guid = Decode(info.GetString("Guid"));
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Guid", Encode(Guid));
}
}
Which works, but if I serialize it:
var guid = new ShortGuid(Guid.NewGuid());
var str = JsonConvert.Serialize(guid);
The serialized string I get looks like this:
{ Guid: "xxxxxxxxxxxxxxxxxx" }
While the serialized string I WANT is just
xxxxxxxxxxxxxxxxxx
I've turned everything around, but can't get this to work. How can this be done?
NOTE: I don't want this to work only on JsonConvert, this is only an example. I would like the class the always be serialized correctly.

this work perfectly for me:
public class GuidShortGuidConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Guid);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var shortGuid = new ShortGuid(reader.Value.ToString());
return shortGuid.Guid;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var customValue = new ShortGuid((Guid) value);
writer.WriteValue(customValue.ToString());
}
}

What you want is not about serialization but implicit casting.
Serialization works on structured key/value base depending on serialization type.
With implicit string casting
public static implicit operator string(ShortGuid shortGuid)
{
return Encode(sh.Guid);
}
You can use
var guid = new ShortGuid(Guid.NewGuid());
string str = guid;

Related

How can I customize the deserialization of a nested property in Json.net without adding attributes

Suppose I have these classes:
public class Bar
{
public Foo MyFoo { get; set; }
}
public class Foo
{
public string[] Stuff { get; set; }
}
And I have this JSON structure:
{
"MyFoo":
{
"Stuff":"those,are,my,stuff"
}
}
And I have a code path where a JObject is being converted to Bar using code like below:
myJObject.ToObject(typeof(Bar))
Now what I need to do is to supply the ToObject with a custom serializer to convert the string property Stuff into an array of string (using string.Split(...).ToArray())
I was asked not to add attributes on the client class 'Bar' so after looking around it seemed like a ContractResolver is in order but the issue is that the resolver only lets me handle direct properties of the root Type, that is Bar in my example, and I can't register a JsonConverter on a nested property.
So my question to you guys is, is this even achievable using Json.net?
Note that I need to do this not only for the Bar class but to an unlimited amount of classes with unknown structure so I can't hard-code a solution that will work for one type of class.
Based on your description, I don't see why you would need a ContractResolver. You know that the properties you want to deserialize specially will always be of type string[], so just make a converter that handles that type. Maybe something like this:
public class CsvStringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string[]);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Null)
return null;
if (token.Type == JTokenType.String)
return ((string)token).Split(',');
if (token.Type == JTokenType.Array)
return token.ToObject<string[]>(serializer);
throw new JsonException("Unexpected token type: " + token.Type);
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then, to use it with your JObject, create a new JsonSerializer instance, add the converter to it, and pass the serializer to the ToObject() method like this:
var serializer = new JsonSerializer();
serializer.Converters.Add(new CsvStringConverter());
var bar = myJObject.ToObject<Bar>(serializer);
Working demo here: https://dotnetfiddle.net/qmeBoh

How to Serialize SqlHierarchyId to String

I have a table with a SqlHierarchyId field type.
The default JSON serialization returns a javascript object that looks like this: {isNull: false} instead of a string like this /1/
Is there some way to set the serialization properly?
public class MyClass
{
[SerializeToString]
public SqlHierarchyId NodeId { get; set; }
}
According to the MSDN documentation, SqlHierarchyId implements both a ToString() method and a static Parse() method to facilitate converting it to a canonical string representation and back. So, assuming you are using Json.Net for serialization, you should be able to make a straightforward JsonConverter class to bridge the gap. Here is what it would look like:
public class SqlHierarchyIdConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(SqlHierarchyId));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
string id = (string)reader.Value;
return (id == null || id == SqlHierarchyId.Null.ToString()) ? SqlHierarchyId.Null : SqlHierarchyId.Parse(id);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
}
To use it, you can either mark the SqlHierarchyId properties in your classes with a [JsonConverter] attribute like this:
class MyClass
{
[JsonConverter(typeof(SqlHierarchyIdConverter))]
public SqlHierarchyId NodeId { get; set; }
}
Or, alternatively, you can pass an instance of the converter as a parameter to SerializeObject or DeserializeObject:
string json = JsonConvert.SerializeObject(myClass, new SqlHierarchyIdConverter());
Here is a round-trip demo: https://dotnetfiddle.net/7h7E82

Convert specific objects to dictionaries before serialization

I'm using Json.NET to serialize validation data for data field. On the .NET side, validation data is a list of ValidationAttribute objects. However, I'd like to serialize them in a special form like this:
[
{ Type: 'Required', ErrorMessage: '{FieldName} is required' },
{ Type: 'RegularExpression', Pattern: '^\d+$', ErrorMessage: '...'
]
In an ideal solution I could simply intercept the object before serialization and, I could create a corresponding Dictionary<string, object> object to serialize instead of the original one.
Are there any solutions for this scenario?
You can implement your own JsonConverter class and convert your collection as you wish.
You just need to create you class and inherit it from JsonConverter
public class YourSerializer : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return typeof(YourClassName).IsAssignableFrom(objectType);
}
}
and then you need to decorate your class which will be serialized with the attribute (looks like it's not what you want)
[JsonConverter(typeof(YourSerializer))]
public class YourClassName
{
public string Name { get; set; }
public string Value { get; set; }
}
or, pass an instance of your serializer to Serialize methos:
string json = JsonConvert.SerializeObject(sourceObj, Formatting.Indented, new YourSerializer(typeof(yourClassName)));
Here is a few links:
http://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm
http://blog.maskalik.com/asp-net/json-net-implement-custom-serialization/
Hope, it will help.

JsonConverter CanConvert does not receive type

I have a custom JsonConverter, which doesn't seem to be called correctly. I have created the converter, added it to the JsonSerializerSettings.Converters collection and marked the property on the entity I am serialising with [JsonConverter(typeof(SearchGeoConverter))], but even with these in place the converters CanConvert method never sees the type I am trying to convert. I only ever see, string, int and JObject.
My converter looks like this:
public class SearchGeoConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(DbGeography).IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var point = (DbGeography) value;
var rawJson = string.Format("{{ \"type\": \"Point\", \"coordinates\": [{0}, {1}] }}", point.Latitude, point.Longitude);
writer.WriteRaw(rawJson);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
What am I missing?
CanConvert does not get called when you mark something with [JsonConverter]. When you use the attribute, Json.Net assumes you have provided the correct converter, so it doesn't bother with the CanConvert check. If you remove the attribute, then it will get called by virtue of you passing the converter instance to the settings. What you are seeing is Json.Net testing your converter for all the other property types.
EDIT
I put together a quick fiddle to show what I mean (code is also reproduced below for completeness).
With no changes the program, CanConvert() gets called on the FooConverter for all types except Foo, yet it still converts Foo correctly.
If you comment out the [JsonConverter] attribute on the Wrapper.Foo property, you can see that CanConvert() will now get called for type Foo by virtue of the FooConverter being included in the JsonSerializerSettings.
If you instead comment out the line in Main where the FooConverter is added to the settings, then CanConvert is never called for any type, yet Foo is still converted correctly due to the [JsonConverter] attribute applied to the Foo property in the Wrapper class.
So the takeaway here is that there are two mechanisms for indicating whether a converter should be used, and you don't need both. You can apply an attribute, and that will tell Json.Net that a particular converter should be used for a particular property (or class) and it does not need to ask the converter first. Alternatively, you can add the converter to the settings, in which case Json.Net has to ask each converter whether it can handle each type. The former is a bit more efficient, while the latter is useful in situations where you don't own the source code for the class you're trying to convert. Hope this makes sense.
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
public class Program
{
public static void Main()
{
JsonSerializerSettings settings = new JsonSerializerSettings();
// Comment out the following line and CanConvert() never gets called on
// FooConverter for any type yet the FooConverter is still working due
// to the JsonConverter attribute applied to Wrapper.Foo
settings.Converters.Add(new FooConverter());
settings.Converters.Add(new BarConverter());
settings.Formatting = Formatting.Indented;
Wrapper w = new Wrapper
{
Foo = new Foo
{
A = "bada",
B = "boom",
},
Bar = new Bar
{
C = "bada",
D = "bing"
}
};
string json = JsonConvert.SerializeObject(w, settings);
Console.WriteLine(json);
}
class Wrapper
{
// Comment out this attribute and CanConvert will be called on FooConverter
// for type Foo due to the fact that the FooConverter has been added to the
// JsonSerializerSettings
[JsonConverter(typeof(FooConverter))]
public Foo Foo { get; set; }
public Bar Bar { get; set; }
}
class Foo
{
public string A { get; set; }
public string B { get; set; }
}
class Bar
{
public string C { get; set; }
public string D { get; set; }
}
class FooConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
bool result = typeof(Foo).IsAssignableFrom(objectType);
Console.WriteLine("FooConverter CanConvert() called for type " +
objectType.Name + " (result = " + result + ")");
return result;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var foo = (Foo) value;
JObject jo = new JObject();
jo.Add("AplusB", new JValue(foo.A + " " + foo.B));
jo.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
class BarConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
bool result = typeof(Bar).IsAssignableFrom(objectType);
Console.WriteLine("BarConverter CanConvert() called for type " +
objectType.Name + " (result = " + result + ")");
return result;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var bar = (Bar) value;
JObject jo = new JObject();
jo.Add("CplusD", new JValue(bar.C + " " + bar.D));
jo.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}

JSON.NET custom constructor

as a partial problem of this question JSON.NET CustomCreationConverter with nested objects I tried to call a custom constructor during deserialization. My simplified class hierarchy is as follows:
public abstract class BusinessObjectBase
{
internal BusinessObjectBase(SerializationContext context)
: base(context)
{
}
}
public abstract class EditableObjectBase : BusinessObjectBase
{
protected EditableObjectBase(SerializationContext context)
: base(context)
{
}
}
public class EditableObjectCollection<TObject> : BusinessObjectBase, ICollection<TObject>, IList, INotifyCollectionChanged where TObject : BusinessObjectBase
{
protected EditableObjectCollection(SerializationContext context)
: base(context)
{
}
}
I know the object hierarchy to a certain level, but users are allowed / forced to derive their own classes. My idea was to write a custom creation converter. The problem I'm fighting with is that a property in a serialized object can be declared as BusinessObjectBase which is abstract, but the real object will be a more derived class and might be a collection or not. A CustomCreationConverter only gets this abstract type passed to the Create method and of course can't create the correct type from this information.
Inspired from this How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects I implemented a converter as follows:
internal class BusinessObjectCreationConverter : JsonConverter
{
public override bool CanWrite
{
get
{
return false;
}
}
public override bool CanConvert(Type objectType)
{
return typeof(BusinessObjectBase).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object result = null;
if (reader.TokenType != JsonToken.Null)
{
JObject jsonObject = JObject.Load(reader);
result = this.Create(objectType, jsonObject);
Verification.Assert<NullReferenceException>(result != null, "No Business Object created.");
serializer.Populate(jsonObject.CreateReader(), result);
}
return result;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
}
public BusinessObjectBase Create(Type objectType, JObject jsonObject)
{
JToken token = jsonObject.SelectToken("$type");
var typeString = token.Value<string>();
Type type = Type.GetType(typeString);
var businessObject = type.CreateUsingDesrializationConstructor<BusinessObjectBase>();
businessObject.Initialize(true);
return businessObject;
}
}
My class to test serialization looks like this:
public class AnyPocoContainingBusinessObject
{
public BusinessObjectBase BusinessObject { get; set; }
}
public class TestEditableObject : EditableObjectBase
{
internal TestEditableObject(SerializationContext context)
: base(context)
{
}
}
If I initialize my class my class with an collection
var collection = new EditableObjectCollection<TestEditableObject>(null);
var poco = new AnyPocoContainingBusinessObject { BusinessObject = collection };
and configure the serializer this way:
public NewtonsoftJsonSerializer()
: this(new JsonSerializer
{
TypeNameHandling = TypeNameHandling.Auto,
ObjectCreationHandling = ObjectCreationHandling.Replace,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
DefaultValueHandling = DefaultValueHandling.Ignore,
ContractResolver = new KsJsonContractResolver()
})
{
this.serializer.Converters.Add(new ReadOnlyObjectCollectionConverter());
this.serializer.Converters.Add(new BusinessObjectCreationConverter());
this.serializer.TraceWriter = new ConsoleTraceWriter();
}
I get an exception:
Cannot populate JSON object onto type 'KS.Interfaces.Core.Entities.EditableObjectCollection`1[KS.Interfaces.Core.Entities.Tests.Unit.EditableObjectCollectionTests+TestEditableObject]'. Path '$type', line 1, position 47.
in this code line of my converter:
serializer.Populate(jsonObject.CreateReader(), result);
Can any body tell me what might be the reason? I'm pretty sure that I created the correct type and with a EditableObjectBase derived object everything is fine. Only collections doesn't seem to work.
Any hints are highly appreciated, thank in advance
Carsten
even I haven't found a way to make my converter work there is one thing I learned during debugging the problem:
It seems that a converter should return an object that still has the same JsonToken value. In my case the JsonToken of the original object was JsonToken.Object, but for my conversion result the correct token value would be JsonToken.Array, but the reader still sees JsonToken.Object. At this point I stopped my research since I found a better way to call my custom constructor.
I wrote my own contract resolver:
internal class BusinessBaseContractResolver : DefaultContractResolver
{
public BusinessBaseContractResolver()
{
this.DefaultMembersSearchFlags |= BindingFlags.NonPublic;
}
public override JsonContract ResolveContract(Type type)
{
JsonContract contract = base.ResolveContract(type);
if (typeof(BusinessObjectBase).IsAssignableFrom(type))
{
contract.DefaultCreator = delegate
{
var businessObject = type.CreateUsingDeserializationConstructor<BusinessObjectBase>();
businessObject.Initialize(true);
return businessObject;
};
}
return contract;
}
}
I hope this helps someone.
Best regards,
Carsten

Categories