When creating a JavascriptConverter, how to return an array?
Asked Answered
E

1

2

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;
    }
Elum answered 27/2, 2009 at 20:39 Comment(2)
What do you plan to do with an Array that you cannot do with an object?Commodore
Thanks for the clarification, though I think my answer still stands. You can't do that, you'll need to convert it to a regular .NET array first.Holy
H
9

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);
Holy answered 27/2, 2009 at 20:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.