创建JavascriptConverter时,如何返回数组?

发布于 2024-07-14 11:19:18 字数 2231 浏览 7 评论 0原文

我正在尝试编写一个自定义 JavascriptConverter 以便与我正在编写的 WebService 一起使用。 我必须编写一个自定义转换器,因为 Microsoft.JScript.JSObject 不支持 IDictionary,因此它被视为数组。 我这部分工作正常。 但是,由于 Microsoft.JScript.ArrayObject 是 Microsoft.JScript.JSObject 的子类,因此它也尝试使用相同的方法对其进行转换。 如何返回将被序列化为 JSON 数组的内容? 我必须返回一个 IDictionary,它将成为一个 JSON 对象。 我有什么遗漏的吗?

具体来说,如何从 Serialize 返回被序列化为数组而不是对象的内容。

谢谢!

编辑:

我想我需要更具体。

Microsoft.JScript.JSObject 实现 IEnumerable,但不实现 IDictionary。 Microsoft.JScript.ArrayObject 是 Microsoft.JScript.JSObject 的子类。 如果我在 JScript.NET 中创建 .asmx,当我尝试返回 JSObject 时,它会被序列化为数组(因为它实现 IEnumerable,但不是 IDictionary),这意味着只有对象中的属性名称才会被序列化。 当然,如果我交回一个 ArrayObject,它会正常工作,并将其序列化为数组。

因此,我实现了 JavascriptConverter 的一个子类,它表示它的 SupportedType 是 Microsoft.JScript.JSObject。 现在,JSObject 已正确序列化。 但是,由于 ArrayObject 是 JSObject 的子类,JavascriptSerializer 不再负责序列化 ArrayObject 本身,而是将其交给 JSObject 序列化器。 问题是,我的自定义序列化程序如何返回 JavaScriptSerializer 将正确视为数组而不是对象的内容 - Serialize 被迫返回 IDictionary,并且它被序列化为对象,而不是数组。

这个问题是不是更有意义呢?

顺便说一句:我已经研究了 WCF 和 C#,但是 DataContractJsonSerializer 输出完全没有用,除非您只使用 WCF 客户端访问它; 我打算使用 jQuery 检索它。

回答:

我终于明白了 bdukes 的意思,而且有效! 返回字典时,您需要告诉它它是一个数组(尽管这对于顶级数组不起作用)。 这是我要编写的 Serialize 函数:

   public override IDictionary<string, object> Serialize (object obj, JavaScriptSerializer serializer) {
        JSObject jsobj = obj as JSObject;
        Dictionary<string, object> netdict = new Dictionary<string, object>();

        if (jsobj != null) {
            foreach (string prop in jsobj) {
                object value = jsobj.GetField(prop, BindingFlags.Default).GetValue(jsobj);
                switch (value.GetType().FullName) {
                    case "Microsoft.JScript.ArrayObject":
                        object[] arr_obj = ((IEnumerable)(Microsoft.JScript.ArrayObject)value).Cast<object>().ToArray<object>();
                        netdict.Add(prop, arr_obj);
                        break;
                    default:
                        netdict.Add(prop, value);
                        break;
                }
            }
        }
        return netdict;
    }

I'm trying to write a custom JavascriptConverter for use with a WebService I'm writing. I have to write a custom converter, because Microsoft.JScript.JSObject doesn't support IDictionary, so it gets treated as an array. I have this part working fine. However, because Microsoft.JScript.ArrayObject is a subclass of Microsoft.JScript.JSObject, it also trys to convert it using the same method. How can I return something that will be serialized as a JSON array? I have to return an IDictionary, which will become a JSON object. Is there something I'm missing?

Specifically, how do I return something from Serialize that gets serialized as an Array, not as an Object.

Thanks!

EDIT:

I guess I need to be more specific.

Microsoft.JScript.JSObject implements IEnumerable, but not IDictionary. Microsoft.JScript.ArrayObject is a subclass of Microsoft.JScript.JSObject. If I create a .asmx in JScript.NET, when I try to return a JSObject, it gets serialized as an array (because it implements IEnumerable, but not IDictionary), which means that only the property names in the object get serialized. Of course, If I hand back an ArrayObject, it works properly, and serializes it as an array.

So, I implemented a subclass of JavascriptConverter, which says that its SupportedType is Microsoft.JScript.JSObject. Now, JSObjects get properly serialized. However, because ArrayObject is a subclass of JSObject, JavascriptSerializer no longer takes care of serializing ArrayObjects itself, it hands it off to the JSObject serializer. The question is, how does my custom serializer return something that the JavaScriptSerializer will properly treat as an array, not as an object -- Serialize is forced to return an IDictionary, and that gets serialized as an object, not an array.

Does this question make more sense?

BTW: I've looked at WCF and C# instead, but the DataContractJsonSerializer output is completely useless unless you're going to only ever access it using a WCF client; I'm planning to retrieve it using jQuery.

Answer:

I finally understood what bdukes was trying to say, and it works! You need to tell it it's an array when returning the dictionary (although this won't work for a top-level array). Here is the Serialize function that I would up writing:

   public override IDictionary<string, object> Serialize (object obj, JavaScriptSerializer serializer) {
        JSObject jsobj = obj as JSObject;
        Dictionary<string, object> netdict = new Dictionary<string, object>();

        if (jsobj != null) {
            foreach (string prop in jsobj) {
                object value = jsobj.GetField(prop, BindingFlags.Default).GetValue(jsobj);
                switch (value.GetType().FullName) {
                    case "Microsoft.JScript.ArrayObject":
                        object[] arr_obj = ((IEnumerable)(Microsoft.JScript.ArrayObject)value).Cast<object>().ToArray<object>();
                        netdict.Add(prop, arr_obj);
                        break;
                    default:
                        netdict.Add(prop, value);
                        break;
                }
            }
        }
        return netdict;
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

潜移默化 2024-07-21 11:19:19

JavaScriptConverters 只能创建 JSON 对象,不能创建其他类型。 如果您只想返回一个数组,则需要将对象转换为 .NET 数组,然后将其发送到 Serialize 方法。

例如,要返回 Person 对象的数组,请执行以下操作:

IList<Person> people = ...;
var serializer = new JavaScriptSerializer();
serializer.Serialize(people.ToArray());

或者,如果您正在创建 JSON 对象并希望其属性之一是数组,则应该使用自定义 JavaScriptConverter,如下所示:

public class ExampleConverter : JavaScriptConverter
{
    /// <summary>
    /// Gets a collection of the supported types
    /// </summary>
    /// <value>An object that implements <see cref="IEnumerable{T}"/> that represents the types supported by the converter. </value>
    public override IEnumerable<Type> SupportedTypes
    {
        get
        {
            return new ReadOnlyCollection<Type>(new Type[] { typeof(MyExampleType) });
        }
    }

    /// <summary>
    /// Converts the provided dictionary into an object of the specified type. 
    /// </summary>
    /// <param name="dictionary">An <see cref="IDictionary{TKey,TValue}"/> instance of property data stored as name/value pairs. </param>
    /// <param name="type">The type of the resulting object.</param>
    /// <param name="serializer">The <see cref="JavaScriptSerializer"/> instance. </param>
    /// <returns>The deserialized object. </returns>
    /// <exception cref="InvalidOperationException">We only serialize</exception>
    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        throw new InvalidOperationException("We only serialize");
    }

    /// <summary>
    /// Builds a dictionary of name/value pairs
    /// </summary>
    /// <param name="obj">The object to serialize. </param>
    /// <param name="serializer">The object that is responsible for the serialization. </param>
    /// <returns>An object that contains key/value pairs that represent the object’s data. </returns>
    /// <exception cref="InvalidOperationException"><paramref name="obj"/> must be of the <see cref="MyExampleType"/> type</exception>
    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        MyExampleType example = obj as MyExampleType;
        if (example == null)
        {
            throw new InvalidOperationException("object must be of the MyExampleType type");
        }

        IDictionary<string, object> jsonExample = new Dictionary<string, object>();
        jsonExample.Add("arrayMember", example.People.ToArray());
        jsonExample.Add("otherMember", example.Member);

        return jsonExample;
    }
}

这将被调用像这样:

JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new ExampleConverter() });
return serializer.Serialize(myExample);

JavaScriptConverters can only create JSON objects, not other types. If you want to return just an array, you'll need to convert to object to a .NET array, and then send that to the Serialize method.

For example, to return an array of Person objects, do something like this:

IList<Person> people = ...;
var serializer = new JavaScriptSerializer();
serializer.Serialize(people.ToArray());

Alternatively, if you're creating a JSON object and want one of its properties to be an array, you should use a custom JavaScriptConverter, like so:

public class ExampleConverter : JavaScriptConverter
{
    /// <summary>
    /// Gets a collection of the supported types
    /// </summary>
    /// <value>An object that implements <see cref="IEnumerable{T}"/> that represents the types supported by the converter. </value>
    public override IEnumerable<Type> SupportedTypes
    {
        get
        {
            return new ReadOnlyCollection<Type>(new Type[] { typeof(MyExampleType) });
        }
    }

    /// <summary>
    /// Converts the provided dictionary into an object of the specified type. 
    /// </summary>
    /// <param name="dictionary">An <see cref="IDictionary{TKey,TValue}"/> instance of property data stored as name/value pairs. </param>
    /// <param name="type">The type of the resulting object.</param>
    /// <param name="serializer">The <see cref="JavaScriptSerializer"/> instance. </param>
    /// <returns>The deserialized object. </returns>
    /// <exception cref="InvalidOperationException">We only serialize</exception>
    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        throw new InvalidOperationException("We only serialize");
    }

    /// <summary>
    /// Builds a dictionary of name/value pairs
    /// </summary>
    /// <param name="obj">The object to serialize. </param>
    /// <param name="serializer">The object that is responsible for the serialization. </param>
    /// <returns>An object that contains key/value pairs that represent the object’s data. </returns>
    /// <exception cref="InvalidOperationException"><paramref name="obj"/> must be of the <see cref="MyExampleType"/> type</exception>
    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        MyExampleType example = obj as MyExampleType;
        if (example == null)
        {
            throw new InvalidOperationException("object must be of the MyExampleType type");
        }

        IDictionary<string, object> jsonExample = new Dictionary<string, object>();
        jsonExample.Add("arrayMember", example.People.ToArray());
        jsonExample.Add("otherMember", example.Member);

        return jsonExample;
    }
}

This gets called like this:

JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new ExampleConverter() });
return serializer.Serialize(myExample);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文