使用JavaScriptSerializer序列化字典

10

显然,IDictionary<string,object> 被序列化为 KeyValuePair 对象的数组(例如,[{Key:"foo", Value:"bar"}, ...])。是否可以将其序列化为对象而不是数组(例如,{foo:"bar"})?


3
好的,我会尽力进行翻译。请注意,原文中可能存在口头语和俚语,为了更好地传达原意,我的翻译可能会使用相应的中文表达方式。请确认以下翻译是否符合您的要求:不要使用 JavaScriptSerializer,它非常糟糕。使用 Newtonsoft Json.NET。 - Andrew Bullock
5个回答

12

虽然我同意JavaScriptSerializer是垃圾,Json.Net是更好的选择,但有一种方式可以让JavaScriptSerializer按照你想要的方式进行序列化。 你需要注册一个转换器,并使用类似于以下代码重写Serialize方法:

    public class KeyValuePairJsonConverter : JavaScriptConverter
{
    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        var instance = Activator.CreateInstance(type);

        foreach (var p in instance.GetType().GetPublicProperties())
        {
            instance.GetType().GetProperty(p.Name).SetValue(instance, dictionary[p.Name], null);
            dictionary.Remove(p.Name);
        }

        foreach (var item in dictionary)
            (instance).Add(item.Key, item.Value);

        return instance;
    }
    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        var result = new Dictionary<string, object>();
        var dictionary = obj as IDictionary<string, object>;
        foreach (var item in dictionary)
            result.Add(item.Key, item.Value);
        return result;
    }
    public override IEnumerable<Type> SupportedTypes
    {
        get
        {
            return new ReadOnlyCollection<Type>(new Type[] { typeof(your_type) });
        }
    }
}

JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
javaScriptSerializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoJsonConverter() });
jsonOfTest = javaScriptSerializer.Serialize(test);
// {"x":"xvalue","y":"\/Date(1314108923000)\/"}
希望这能有所帮助!

我正在尝试使用你的类,但似乎你的“Class Object”的instance变量没有添加方法。 - artifex_somnia

4

不,使用JavaScriptSerializer是不可能的。可以使用Json.NET实现:

public class Bar
{
    public Bar()
    {
        Foos = new Dictionary<string, string>
        {
            { "foo", "bar" }
        };
    }

    public Dictionary<string, string> Foos { get; set; }
}

然后:
var bar = new Bar();
string json = JsonConvert.SerializeObject(bar, new KeyValuePairConverter());

会产生所需的结果:

{"Foos":{"foo":"bar"}}

我一开始也是走了这条路。请参考我的先前问题:https://dev59.com/8Gw15IYBdhLWcg3wv-ba。我在使用Json.NET反序列化嵌套字典时遇到了问题。 - Daniel
2
@Daniel,与JavaScriptSerializer相比,Json.NET就像糖一样甜。所以如果你在使用Json.NET时遇到问题,我不知道该说什么关于替代方案的事情了 :-) 至于你的另一个问题,那是完全正常的行为。你向序列化程序指示的只是一个字符串和对象的字典。因此,在你的代码中,你正在使用弱类型字典,你期望得到什么回报呢?当然,你只能得到弱类型的JObjects。使用强类型字典:Dictionary<string, SomeModel>,会有所不同。 - Darin Dimitrov
你指的是什么区别?我没有看到。 - Daniel
抱歉,我只是在看 DictionaryIDictionary 之间的区别--明白了。谢谢。 - Daniel

3
我使用JavaScriptSerializer和Linq Select解决了这个问题:
var dictionary = new Dictionary<int, string>();
var jsonOutput = new JavaScriptSerializer().Serialize(dictionary.Select(x => new { Id = x.Key, DisplayText = x.Value  }));

这在复杂情况下(即复杂的分层结构)无法工作,但它确实解决了简单情况下的问题,因此如果您想避免向JavaScriptSerializer或第三方库添加样板代码,则可以考虑使用它作为解决方案。 - Joan Bruguera
对我来说,即使在复杂的情况下,只需更改为.Serialize(dictionary),它也能正常工作。它比JsonConvert更简单,可以直接使用。我希望我能给这个答案+2。 - Bernardo Dal Corno

1
我使用JavaScriptSerializer解决了它,诀窍是创建自己的转换器。以下代码是可行的代码:
public class KeyValuePairJsonConverter : JavaScriptConverter {
    public override object Deserialize(IDictionary<string, object> dictionary
                                        , Type type
                                        , JavaScriptSerializer serializer) {
        throw new InvalidOperationException("Sorry, I do serializations only.");
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) {
        Dictionary<string, object> result = new Dictionary<string, object>();
        Dictionary<string, MyClass> dictionaryInput = obj as Dictionary<string, MyClass>;

        if (dictionaryInput == null) {
            throw new InvalidOperationException("Object must be of Dictionary<string, MyClass> type.");
        }

        foreach (KeyValuePair<string, MyClass> pair in dictionaryInput)
            result.Add(pair.Key, pair.Value);

        return result;
    }

    public override IEnumerable<Type> SupportedTypes {
        get {
            return new ReadOnlyCollection<Type>(new Type[] { typeof(Dictionary<string, MyClass>) });
        }
    }
}

这是如何使用它的:

JavaScriptSerializer js = new JavaScriptSerializer();
js.RegisterConverters(new JavaScriptConverter[] { new KeyValuePairJsonConverter() });
Context.Response.Clear();
Context.Response.ContentType = "application/json";
Context.Response.Write(js.Serialize(myObject));

1
这是来自Tomas答案的我认为改进后的版本。非常好用。我们也可以添加ScriptIgnore属性检查,但是,你自己看着办吧。
顺便说一下,我选择JavaScriptSerializer,因为在我看来,第三方解决方案大多数时间:知名度较低,安装时间长,往往会忘记先决条件,并且具有模糊的版权状态,使它们在商业上分发存在风险。
P-S:我不明白为什么我们要尝试将实例反序列化为实例和字典,所以我去掉了那部分。
public class KeyValuePairJsonConverter : JavaScriptConverter
{
    public override object Deserialize(IDictionary<string, object> deserializedJSObjectDictionary, Type targetType, JavaScriptSerializer javaScriptSerializer)
    {
        Object targetTypeInstance = Activator.CreateInstance(targetType);

        FieldInfo[] targetTypeFields = targetType.GetFields(BindingFlags.Public | BindingFlags.Instance);

        foreach (FieldInfo fieldInfo in targetTypeFields)
            fieldInfo.SetValue(targetTypeInstance, deserializedJSObjectDictionary[fieldInfo.Name]);

        return targetTypeInstance;
    }

    public override IDictionary<string, object> Serialize(Object objectToSerialize, JavaScriptSerializer javaScriptSerializer)
    {
       IDictionary<string, object> serializedObjectDictionary = new Dictionary<string, object>();

       FieldInfo[] objectToSerializeTypeFields = objectToSerialize.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);

       foreach (FieldInfo fieldInfo in objectToSerializeTypeFields)
           serializedObjectDictionary.Add(fieldInfo.Name, fieldInfo.GetValue(objectToSerialize));

       return serializedObjectDictionary;
    }

    public override IEnumerable<Type> SupportedTypes
    {
        get
        {
            return new ReadOnlyCollection<Type>(new Type[] { typeof(YOURCLASSNAME) });
        }
    }
}

关于我的PS笔记。也许这是为了允许指定通用字典类型而不是类型,但老实说,我不明白指定通用字典类型的意义,因为整个过程都是关于反序列化<String,XYZ>字典的。而且这很麻烦。 - Olograph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接