开发者

Serializing Linq2sql model selectively to JSON

开发者 https://www.devze.com 2023-04-11 17:56 出处:网络
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 serial

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...

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号