2008年12月28日日曜日

DataContractJsonSerializerでDictionaryを表現

DataContractJsonSerializerはまじめなので、.NETとJSONとの双方向で型が復元されることを前提にしています。そのため、Dictionary<TKey,TValue>を素直にobject表現してくれません。(TKeyがstringに限定され、intなどが復元できないため。TValueの型が不明なため。)
具体的にどうなるかというと

new Dictionary<string,object>{ { "abc", 1 }, { "def", "ghi" } };
[{"Key":"abc","Value":1},{"Key":"def","Value":"ghi"}]
になります。
一般的にJSONを扱う人は
{"abc":1,"def":"ghi"}
を期待していることでしょう。

どうにかしたいと思い、必死に対策を考え、遂に実現しました。
class Surrogate: IDataContractSurrogate {
[Serializable]
class JsonDictionary: ISerializable {
Dictionary<string, object> Dictionary { get; set; }
public JsonDictionary( Dictionary<string, object> dictionary ) {
Dictionary = dictionary;
}
public void GetObjectData( SerializationInfo info, StreamingContext context ) {
foreach( var pair in Dictionary )
info.AddValue( pair.Key, pair.Value );
}
}

public Type GetDataContractType( Type type ) {
return type == typeof( Dictionary<string, object> ) ? typeof( JsonDictionary ) : type;
}
public object GetObjectToSerialize( object obj, Type targetType ) {
var dictionary = obj as Dictionary<string, object>;
if( obj == null )
throw new NotImplementedException();
return new JsonDictionary( dictionary );
}

#region NotImplemented
object IDataContractSurrogate.GetCustomDataToExport( Type clrType, Type dataContractType ) {
throw new NotImplementedException();
}
object IDataContractSurrogate.GetCustomDataToExport( MemberInfo memberInfo, Type dataContractType ) {
throw new NotImplementedException();
}
object IDataContractSurrogate.GetDeserializedObject( object obj, Type targetType ) {
throw new NotImplementedException();
}
void IDataContractSurrogate.GetKnownCustomDataTypes( Collection<Type> customDataTypes ) {
throw new NotImplementedException();
}
Type IDataContractSurrogate.GetReferencedTypeOnImport( string typeName, string typeNamespace, object customData ) {
throw new NotImplementedException();
}
CodeTypeDeclaration IDataContractSurrogate.ProcessImportedType( CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit ) {
throw new NotImplementedException();
}
#endregion
}

あとはDataContractJsonSerializerコンストラクタのIDataContractSurrogate引数にSurrogateインスタンスを渡せば、変換してくれます。
もちろん型保証が外れるため、deserializeはできません。

0 件のコメント: