Как вернуть массив при создании JavascriptConverter?

Я пытаюсь написать собственный 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. Если я создаю .asmx в JScript.NET, когда я пытаюсь вернуть JSObject, он сериализуется как массив (потому что он реализует IEnumerable, но не IDictionary), что означает, что сериализуются только имена свойств в объекте. Конечно, если я верну объект ArrayObject, он будет работать правильно и сериализует его как массив.

Итак, я реализовал подкласс JavascriptConverter, который говорит, что его SupportedType - это Microsoft.JScript.JSObject. Теперь объекты JSO правильно сериализуются. Однако, поскольку 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;
    }

Ответов (1)

Решение

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);