2014-11-07 9 views
10

Quindi, ho un sacco di classi che ho bisogno di serializzare/deserializzare che anche capita di essere oggetti di dominio (almeno alcuni di essi), quindi li voglio a essere libero da qualsiasi attributo o meno a seconda di un certo struttura.Convertitori/mappatori fluidi con Json.NET?

ho guardato Convertitori personalizzati in Json.NET, ma hanno un aspetto molto 'antica' a me in termini di utilizzo e cosa no, dal momento che al giorno d'oggi sono generici e non è così difficile per implementare un interfaccia fluente. Quindi, prima di Vado giù per la strada di tipizzazione debole ecc ..

... quello che sto cercando (pseudo):

public class MyModel { 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public SomeObj SomeObj { get; set; } 
} 

public class MyModelConverter : JsonConverter<MyModel> { 

    public JsonConverter() { 
     RuleFor(x => x.Id).Name("Identifier"); 
     RuleFor(x => x.SomeObj).Name("Data") 
      .WithConverter(new SomeObjConverter()); 
     RuleFor(x => x.Name).Ignore(); 
    } 

} 

C'è qualcosa di simile in Json.NET? Mi manca qualcosa? (A proposito, non posso usare nomi diversi per le mie proprietà ecc. Poiché i modelli sono basati su specifiche di terze parti).

+2

magari utilizzare 'AutoMapper' e versare l'oggetto dominio nella classe desiderata? Ha una sintassi * ver * vicino a ciò che stai cercando. – code4life

+0

Metodi di estensione e rilascia il tuo plugin di potenziamento per shim JSON.NET! :-) – VulgarBinary

+0

Potresti semplicemente creare un nuovo tipo anonimo e poi serializzarlo. '... new {Identifier = x.Id, ...}' –

risposta

2

Ecco il mio prendere sul raggiungimento API desiderata:

cura come da commento

public abstract class Rule 
{ 
    private Dictionary<string, object> rule { get; } = new Dictionary<string, object>(); 

    protected void AddRule(string key, object value) 
    { 
     if (rule.ContainsKey(key)) 
     { 
      rule.Add(key, value); 
     } 
     else 
     { 
      rule[key] = value; 
     } 
    } 

    protected IEnumerable<KeyValuePair<string, object>> RegisteredRules 
    { 
     get 
     { 
      return rule.AsEnumerable(); 
     } 
    } 
} 

public abstract class PropertyRule : Rule 
{ 
    public MemberInfo PropertyInfo { get; protected set; } 

    public void Update(JsonProperty contract) 
    { 
     var props = typeof(JsonProperty).GetProperties(); 
     foreach (var rule in RegisteredRules) 
     { 
      var property = props.Where(x => x.Name == rule.Key).FirstOrDefault(); 
      if (property != null) 
      { 
       var value = rule.Value; 
       if (property.PropertyType == value.GetType()) 
       { 
        property.SetValue(contract, value); 
       } 
      } 
     } 
    } 
} 

public class PropertyRule<TClass, TProp> : PropertyRule 
{ 
    public const string CONVERTER_KEY = "Converter"; 
    public const string PROPERTY_NAME_KEY = "PropertyName"; 
    public const string IGNORED_KEY = "Ignored"; 

    public PropertyRule(Expression<Func<TClass, TProp>> prop) 
    { 
     PropertyInfo = (prop.Body as System.Linq.Expressions.MemberExpression).Member; 
    } 

    public PropertyRule<TClass, TProp> Converter(JsonConverter converter) 
    { 
     AddRule(CONVERTER_KEY, converter); 
     return this; 
    } 

    public PropertyRule<TClass, TProp> Name(string propertyName) 
    { 
     AddRule(PROPERTY_NAME_KEY, propertyName); 
     return this; 
    } 

    public PropertyRule<TClass, TProp> Ignore() 
    { 
     AddRule(IGNORED_KEY, true); 
     return this; 
    } 
} 

public interface SerializationSettings 
{ 
    IEnumerable<Rule> Rules { get; } 
} 

public class SerializationSettings<T> : SerializationSettings 
{ 
    private List<Rule> rules { get; } = new List<Rule>(); 

    public IEnumerable<Rule> Rules { get; private set; } 

    public SerializationSettings() 
    { 
     Rules = rules.AsEnumerable(); 
    } 

    public PropertyRule<T, TProp> RuleFor<TProp>(Expression<Func<T, TProp>> prop) 
    { 
     var rule = new PropertyRule<T, TProp>(prop); 
     rules.Add(rule); 
     return rule; 
    } 
} 

public class FluentContractResolver : DefaultContractResolver 
{ 
    static List<SerializationSettings> settings; 

    public static void SearchAssemblies(params Assembly[] assemblies) 
    { 
     SearchAssemblies((IEnumerable<Assembly>)assemblies); 
    } 

    public static void SearchAssemblies(IEnumerable<Assembly> assemblies) 
    { 
     settings = assemblies.SelectMany(x => x.GetTypes()).Where(t => IsSubclassOfRawGeneric(t, typeof(SerializationSettings<>))).Select(t => (SerializationSettings)Activator.CreateInstance(t)).ToList(); 
    } 

    //http://stackoverflow.com/questions/457676/check-if-a-class-is-derived-from-a-generic-class 
    static bool IsSubclassOfRawGeneric(System.Type toCheck, System.Type generic) 
    { 
     if (toCheck != generic) 
     { 
      while (toCheck != null && toCheck != typeof(object)) 
      { 
       var cur = toCheck.GetTypeInfo().IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; 
       if (generic == cur) 
       { 
        return true; 
       } 
       toCheck = toCheck.GetTypeInfo().BaseType; 
      } 
     } 
     return false; 
    } 

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) 
    { 
     var contract = base.CreateProperty(member, memberSerialization); 

     var rule = settings.Where(x => x.GetType().GetTypeInfo().BaseType.GenericTypeArguments[0] == member.DeclaringType).SelectMany(x => x.Rules.Select(r => r as PropertyRule).Where(r => r != null && r.PropertyInfo.Name == member.Name)).FirstOrDefault(); 
     if (rule != null) 
     { 
      rule.Update(contract); 
     } 
     return contract; 
    } 
} 

Ora, in qualche parte nel programma di start-up:

FluentContractResolver.SearchAssemblies(typeof(MyModel).GetTypeInfo().Assembly); 

Newtonsoft.Json.JsonConvert.DefaultSettings =() => 
{ 
    return new Newtonsoft.Json.JsonSerializerSettings 
    { 
     Formatting = Newtonsoft.Json.Formatting.Indented, 
     ContractResolver = new FluentContractResolver() 
    }; 
}; 

Con questo in ora devi solo aggiungere classi con impostazioni fluide:

+0

1. La prima riga in PropertyRule.Update deve essere: var props = typeof (JsonProperty) .GetProperties(); 2. Per .NET Core le proprietà Type IsGenericType, Assembly e BaseType sono accessibili tramite GetTypeInfo(). Appart da tutto il resto va bene. Bella soluzione –

+0

@AlexanderChristov Ho modificato il codice come da tuo commento. Grazie! – Nripendra

1

Fluent-Json.NET consente di mappare oggetti, utilizzare discriminatori di tipo, il tutto senza interferire con gli oggetti dati. Non sono richiesti attributi

classi Mapping

public class AnimalMap : JsonMap<Animal> 
{ 
    public AnimalMap() 
    { 
     this.DiscriminateSubClassesOnField("class"); 
     this.Map(x => x.Speed, "speed"); 
    } 
} 

public class FelineMap : JsonSubclassMap<Feline> 
{ 
    public FelineMap() 
    { 
     this.Map(x => x.SightRange, "sight"); 
    } 
} 

public class LionMap : JsonSubclassMap<Lion> 
{ 
    public LionMap() 
    { 
     this.DiscriminatorValue("lion"); 
     this.Map(x => x.Strength, "strength"); 
    } 
} 

classi del modello

public class Animal 
{ 
    public Animal(float speed) 
    { 
     this.Speed = speed; 
    } 

    public float Speed { get; set; } 
} 

public abstract class Feline : Animal 
{ 
    protected Feline(float speed, float sightRange) : base(speed) 
    { 
     this.SightRange = sightRange; 
    } 

    public float SightRange { get; set; } 
} 

public class Lion : Feline 
{ 
    public Lion(float speed, float sightRange, float strength) : base(speed, sightRange) 
    { 
     this.Strength = strength; 
    } 

    public float Strength { get; set; } 
}