2

If I try to add the JsonDictionary attribute to a .net Dictionary(Of Integer, MyClass), the compiler tells me, that the attribute could not be applied. Why is this?

<JsonDictionary()>
Public ReadOnly Property monatswerte As Dictionary(Of Integer, MyClass)

I basically couldn't find any examples of how to use the JsonDictionary online.

dbc
  • 104,963
  • 20
  • 228
  • 340
André R.
  • 427
  • 7
  • 17
  • 2
    I've never used it, so can't really help, but the [source code](https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/JsonDictionaryAttribute.cs) has `[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)]`, so you can only use it on a class or interface. If (like me) you don't know how to use it, perhaps it's not the answer you are looking for - perhaps you should ask about the problem you are trying to solve instead? :-) – Mark Mar 30 '17 at 16:17
  • I added the [tag:.net] and [tag:c#] tags since the question and answer are not language specific. – dbc Mar 30 '17 at 18:36

2 Answers2

5

<JsonDictionary()> can be applied to a type (a class or interface) to force Json.NET's default contract resolver (and its subclasses) to generate a dictionary contract for the type. It is part of a family of three similar attributes:

  • <JsonObject()>. Force a type to be serialized as a JSON object. Useful to force serialization of collection properties instead of items as shown here.
  • <JsonArray()>. Force a type to serialized as a JSON array. Possibly useful to, e.g., force a dictionary to be serialized as a key/value pair array.
  • <JsonDictionary()>. Force a type to be interpreted as a dictionary and serialized as a JSON object. (It must still implement IDictionary or IDictionary<TKey, TValue> for this to work, of course.)

On the face of it <JsonDictionary()> does not seem that useful because DefaultContractResolver.CreateContract(Type objectType) checks that the incoming type implements IDictionary before checking for any other interface implementations. However, the attribute has several properties that could be useful in customizing how a dictionary is serialized, including:

  • NamingStrategyType and NamingStrategyParameters allow control of casing and name-mapping of dictionary keys.

    For instance, the following dictionary will always serialize its keys literally, without renaming, even if CamelCasePropertyNamesContractResolver is in use:

    <JsonDictionary(NamingStrategyType := GetType(LiteralKeyDictionaryNamingStrategy))> _
    Public Class LiteralKeyDictionary(Of TValue)
        Inherits Dictionary(Of String, TValue)
    End Class
    
    Public Class LiteralKeyDictionaryNamingStrategy
        Inherits DefaultNamingStrategy
    
        Public Sub New()
            ProcessDictionaryKeys = False
        End Sub
    End Class
    
  • ItemConverterType, ItemConverterParameters, ItemIsReference, ItemReferenceLoopHandlingand ItemTypeNameHandling allow customization of how dictionary values are serialized.

    For instance, in the following dictionary type, the values are always stored with reference preservation enabled:

    <JsonDictionary(ItemIsReference := true)> _
    Public Class ReferenceObjectDictionary(of TKey, TValue As {Class, New})
        Inherits Dictionary(Of TKey, TValue)
    End Class
    

    Or, for a dictionary type containing enum values, you might apply StringEnumConverter as an ItemConverterType to force the values to be serialized as strings.

dbc
  • 104,963
  • 20
  • 228
  • 340
0

@dbc's answer is great, +1'd (and this approach is a lot less messing than going the TypeConverter route as it's explicit). Here's an F# riff/rip-off/port:

[<JsonDictionary(NamingStrategyType =
                    typedefof<VerbatimKeyDictionaryNamingStrategy>)>]
type VerbatimKeyDictionary<'value>(values : IDictionary<string,'value>) =
    inherit Dictionary<string,'value>(values)
    override this.Equals other =
        let that = other :?> IDictionary<string,'value>
        that <> null
        && this.Count = that.Count
        && Seq.isEmpty (this |> Seq.except that)
    override __.GetHashCode () = 0
and VerbatimKeyDictionaryNamingStrategy() =
    inherit Newtonsoft.Json.Serialization.DefaultNamingStrategy(
         ProcessDictionaryKeys = false)
Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249