I have quite common linq2sql bussiness model from mssql database. There are some associations between tables, which is good. Whole model is in separate assembly. I am using JSON.NET library for serialization.
Now i need to serialize those models to JSON and tell it which properties to use and which now. Using if attributes is impossible, but i don't like idea of metadata class either.
So i had been thinking about using extension method in this manner:
public static class User {
public stat开发者_如何学Pythonic object GetSerializable(this DataModel.User user) {
return new {
user.Id, user.LoginName, user.FirstName, user.LastName
}
}
}
This would nice, however i am not sure how to use it in cases like this:
[JsonObject]
public class AuthModel {
[JsonProperty]
public DataModel.User { get; set; }
}
Do you have any idea how to effectively use those extensions methods there ? Or some other completely different ideas ?
You can use Fluent Json to convert them to json. This configuration can be done in code without using attributes.
Option 2 : You can use Custom Serializer
Option 3 : You can use the KeyValuePairConverter. Convert your persistant class to a dictionary and use this.
All right i had decided for approach based on custom JsonConverter which is in result very similar to that approach above. It looks like this:
public abstract class SerializeSelectorConverter<TModel> : JsonConverter where TModel: class
{
protected abstract object GetSerializableObject( TModel model );
public override bool CanWrite { get { return true; } }
public override bool CanConvert(Type objectType)
{
return objectType == typeof(TModel);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, GetSerializableObject( value as TModel ));
}
public override bool CanRead { get { return false; } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then i just make a class like:
[JsonObject]
public class AuthModel {
[JsonProperty]
[JsonConverter(typeof(UserConverter))]
public DataModel.User { get; set; }
}
private class UserConverter : SerializeSelectorConverter<DataModel.User>
{
protected override object GetSerializableObject(DataModel.User model)
{
return new
{
model.Id,
model.LoginName,
model.FirstName,
model.LastName
};
}
}
Simple enough without some complex configuration or metadata classes. Everything is properly validated by compiler, so no typos and problems when changes occurs.
I wrote a custom JsonConverter to handle this kind of case generically given that the source class has an interface enumerating what needs to be serialized.
Your class plus serialization interface:
public interface IUser {
Guid Id { get; set; }
string LoginName { get; set; }
...
}
public class User : IUser {
...implementation...
}
The converter:
public class InterfaceExtractorJsonConverter<T> : JsonConverter {
private class InterfaceDictionary<T> : Dictionary<string, object> { }
private PropertyInfo[] InterfaceProperties {
get { return typeof(T).GetProperties(); }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
var dictionary = new InterfaceDictionary<T>();
foreach (var property in InterfaceProperties) {
dictionary[property.Name] = value.GetType().GetProperty(property.Name).GetValue(value, null);
}
serializer.Serialize(writer, dictionary);
}
private object ReadNestedObject(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
while (reader.TokenType == JsonToken.Comment) {
if (!reader.Read())
throw new Exception("Unexpected end.");
}
switch (reader.TokenType) {
case JsonToken.StartObject:
case JsonToken.StartArray:
return serializer.Deserialize(reader, objectType);
case JsonToken.Integer:
case JsonToken.Float:
case JsonToken.String:
case JsonToken.Boolean:
case JsonToken.Null:
case JsonToken.Undefined:
case JsonToken.Date:
case JsonToken.Bytes:
return reader.Value;
default:
throw new Exception(string.Format("Unexpected token when converting object: {0}", reader.TokenType));
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
var obj = Activator.CreateInstance(objectType);
while (reader.Read()) {
switch (reader.TokenType) {
case JsonToken.PropertyName:
string propertyName = reader.Value.ToString();
if (!reader.Read())
throw new Exception("Unexpected end.");
if (!InterfaceProperties.Any(p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase))) {
reader.Skip();
continue;
}
var property = objectType.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
var innerObj = ReadNestedObject(reader, property.PropertyType, existingValue, serializer);
property.SetValue(obj, innerObj, null);
break;
case JsonToken.Comment:
break;
case JsonToken.EndObject:
return obj;
}
}
throw new Exception("Unexpected end.");
}
public override bool CanConvert(Type objectType) {
return objectType.GetInterfaces().Contains(typeof(T));
}
}
A lot of optimizations can be made to the converter...
精彩评论