2014-01-07 5 views
14

Ich habe ein Wörterbuch Dictionary<string, Dictionary<string, object>>. Sowohl das äußere Wörterbuch als auch das innere Wörterbuch haben einen Gleichheitsvergleichssatz (in meinem Fall ist es StringComparer.OrdinalIgnoreCase). Nachdem das Wörterbuch serialisiert und deserialisiert wurde, wird der Vergleich für beide Wörterbücher nicht auf StringComparer.OrdinalIgnoreCase festgelegt.Json.NET Wörterbuch <string,T> mit StringComparer Serialisierung

Wenn Sie die Erstellung der Wörterbücher in Ihrem Code steuern können, können Sie eine vom Wörterbuch geerbte Klasse erstellen und im Standardkonstruktor der Klasse einen Vergleicher festlegen. Aber was ist, wenn Sie keine Kontrolle über die Wörterbucherstellung haben und das Wörterbuch aus dem anderen Code bekommen?

Gibt es eine Möglichkeit, es korrekt mit dem Vergleich serialisieren/deserialisieren?

Antwort

12

Eine einfache Idee wäre, eine Unterklasse von Dictionary<string, string> zu erstellen, die den Vergleich standardmäßig auf StringComparer.OrdinalIgnoreCase setzt, und dann in das anstelle des normalen Wörterbuchs deserialisieren. Zum Beispiel:

class CaseInsensitiveDictionary<V> : Dictionary<string, V> 
{ 
    public CaseInsensitiveDictionary() : base(StringComparer.OrdinalIgnoreCase) 
    { 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     string json = @" 
     { 
      ""Foo"" : 
      { 
       ""fiZZ"" : 1, 
       ""BUzz"" : ""yo"" 
      }, 
      ""BAR"" : 
      { 
       ""dIt"" : 3.14, 
       ""DaH"" : true 
      } 
     }"; 

     var dict = JsonConvert.DeserializeObject<CaseInsensitiveDictionary<CaseInsensitiveDictionary<object>>>(json); 

     Console.WriteLine(dict["foo"]["fizz"]); 
     Console.WriteLine(dict["foo"]["buzz"]); 
     Console.WriteLine(dict["bar"]["dit"]); 
     Console.WriteLine(dict["bar"]["dah"]); 
    } 
} 

Ausgang:

1 
yo 
3.14 
True 
+0

Nun, ich dachte über die gleiche Sache, aber in einigen Fällen haben Sie keine Kontrolle über die Erstellung von Wörterbüchern. Das äußere Wörterbuch kann leicht in das CaseInsensitive Dictionary konvertiert werden, aber um das innere Wörterbuch zu konvertieren, wird es viel Overhead für mich bedeuten. Deshalb suche ich nach einer generischen Lösung, aber nicht nach einer Problemumgehung. – Bug

+1

Vielleicht sollten Sie Ihre Frage bearbeiten, um diese Anforderung anzugeben und weitere Details zu Ihrer Situation anzugeben.Zum Beispiel, wie machst du jetzt die Deserialisierung? Was ist in deiner Kontrolle und was nicht? Haben Sie einen Beispielcode, der Ihnen zeigen kann, was Sie gerade tun oder tun? Je mehr Details Sie angeben, desto besser werden die Antworten, die Sie erhalten. –

+0

Fertig. Danke Brian. – Bug

7

Es wäre besser, einen Konverter zu erstellen, die die Dictionary-Objekte erstellen würden je nach Bedarf. Genau dafür wurde die Newtonsoft.Json.Converters.CustomCreationConverter<T> entwickelt.

Hier ist eine Implementierung, die Wörterbücher erstellen kann, die benutzerdefinierte Vergleiche erfordert.

public class CustomComparerDictionaryCreationConverter<T> : CustomCreationConverter<IDictionary> 
{ 
    private IEqualityComparer<T> comparer; 
    public CustomComparerDictionaryCreationConverter(IEqualityComparer<T> comparer) 
    { 
     if (comparer == null) 
      throw new ArgumentNullException("comparer"); 
     this.comparer = comparer; 
    } 

    public override bool CanConvert(Type objectType) 
    { 
     return HasCompatibleInterface(objectType) 
      && HasCompatibleConstructor(objectType); 
    } 

    private static bool HasCompatibleInterface(Type objectType) 
    { 
     return objectType.GetInterfaces() 
      .Where(i => HasGenericTypeDefinition(i, typeof(IDictionary<,>))) 
      .Where(i => typeof(T).IsAssignableFrom(i.GetGenericArguments().First())) 
      .Any(); 
    } 

    private static bool HasGenericTypeDefinition(Type objectType, Type typeDefinition) 
    { 
     return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeDefinition; 
    } 

    private static bool HasCompatibleConstructor(Type objectType) 
    { 
     return objectType.GetConstructor(new Type[] { typeof(IEqualityComparer<T>) }) != null; 
    } 

    public override IDictionary Create(Type objectType) 
    { 
     return Activator.CreateInstance(objectType, comparer) as IDictionary; 
    } 
} 

Sie jedoch beachten, dass dieser Konverter für alle Wörterbuchtypen gelten, wo der Schlüssel mit T covariant ist, unabhängig von Werttyp.

Dann, es zu benutzen:

var converters = new JsonConverter[] 
{ 
    new CustomComparerDictionaryCreationConverter<string>(StringComparer.OrdinalIgnoreCase), 
}; 
var dict = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, object>>>(jsonString, converters); 
+0

Schön, einen sauberen Weg zu sehen. :-) –

1

eine Erweiterungsmethode erstellen, die die Werte von Ihrem Fall empfindlichen Wörterbuch auf eine neue Groß- und Kleinschreibung Wörterbuch kopiert.

public static Dictionary<string, T> ToCaseInsensitive<T>(this Dictionary<string, T> caseSensitiveDictionary) 
{ 
    var caseInsensitiveDictionary = new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase); 
    caseSensitiveDictionary.Keys.ToList() 
     .ForEach(k => caseInsensitiveDictionary[k] = caseSensitiveDictionary[k]); 

    return caseInsensitiveDictionary; 
} 

Verbrauch:

var newDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(value) 
.ToCaseInsensitive(); 

Obwohl dies funktioniert für mich (und Ich mag diese Lösung aufgrund ihrer Einfachheit), beachten Sie bitte die folgenden Einschränkungen:

  • Es gibt einen kleineren Kopf Angefangen beim Kopieren
  • Wenn Sie doppelte Schlüssel im Wörterbuch haben (wie "cat" und "CAT"), wird einer überschrieben. Sie können die Methode leicht anpassen, um in solchen Fällen eine Ausnahme auszulösen (wenn Sie möchten).
  • Meine Lösung verwendet nicht unbedingt den Comparer während der Deserialisierung, aber ist wahrscheinlich der einfachste Weg, um Ihr Wörterbuch in einen Zustand ohne Berücksichtigung der Groß- und Kleinschreibung zu bringen.
Verwandte Themen