Разрешение типа массива JSON в веб-сервисах ASP.Net - PullRequest
9 голосов
/ 06 января 2011

Итак, я решил, как передать свои пользовательские объекты в веб-сервисы ASP.Net json.Работает шарм.

У меня проблема с передачей прямых массивов моих пользовательских объектов или, альтернативно, передачей массивов, которые являются параметрами моих пользовательских объектов.

Так, например ...

Public Class WebService1
    Inherits System.Web.Services.WebService

    <WebMethod()> _
    <ScriptMethod(ResponseFormat:=ResponseFormat.Json)> _
    Public Function AddPersonList(ByVal PersonList As PersonList) As String
        Debug.Assert(False)
    End Function

    Public Class Person
        Public Sub New()
        End Sub

        Public Property FirstName As String
        Public Property LastName As String
    End Class

    Public Class PersonList
        Inherits List(Of Person)

    End Class
End Class

<script>
        $(function () {
            $.ajax({
                type: "POST",
                url: "WebService1.asmx/AddPersonList",
                data: " { PersonList: [ { FirstName: 'Max', LastName: 'Gershkovich' }, { FirstName: 'Test1', LastName: 'Test2' } ] }",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: function (e) { debugger; },
                error: function (e) { debugger; }
            });
        });
    </script>

Сбой с ошибкой: значение \ "System.Collections.Generic.Dictionary`2 [System.String, System.Object] \" равноне относится к типу \ "WebApplication1.WebService1 + Person \" и не может использоваться в этой универсальной коллекции. \ r \ nИмя параметра: значение

Итак, как мне объяснить ASP.net, что это на самом деле массив массиваPerson?

Обратите внимание: изменение функции на List (of Person) или ArrayList работает, но, учитывая, что я реализую свои собственные пользовательские коллекции, это не оптимально для меня.

ОБНОВЛЕНИЕ: Хорошо, так что я до сих пор работал, что эта проблема определенно связана с тем, как JavascriptSerializer использует SimpleTypeResolver для разрешения типов.В основном, если я делаю что-то вроде этого

Public Function AddPersonList(ByVal PersonList As String) As PersonList

Я могу воссоздать ошибку, используя следующий код.

Dim PersonList As PersonList = jsonSerializer.Deserialize(Of PersonList)(PList)

Однако, когда япредоставить свой собственный распознаватель пользовательских типов в соответствии с

Dim jsonSerializer As New JavaScriptSerializer(New MyCustomTypeResolver)

Я могу успешно создать экземпляр моего пользовательского списка.

Теперь яЯ разработал, как предоставить свой собственный конвертер в файле web.config.В соответствии с этим….

<system.web.extensions>
    <scripting>
      <webServices>
        <jsonSerialization>
          <converters>
            <add name="MyCustomConverter" type="WebApplication1.MyCustomConverter" />
          </converters>
        </jsonSerialization>
      </webServices>      
    </scripting>
  </system.web.extensions>

Но проблема в том, что я не могу найти способ указать CustomTypeResolver.Я предполагаю, что это единственная часть головоломки, которую мне не хватает.

PSS: я пытался использовать полное имя сборки, как указано в документации для SimpleTypeResolver ( SimpleTypeResolver MSDN ), но при этом выдается «Операция недопустима из-за текущего состояния объекта».исключение - ошибка, вызванная тем, что TypeResolver не может разрешить имя (я знаю это по тестированию с моим CustomTypeResolver)

UPDATE @ Oleg и Sean:

Я искренне ценю ваш вклад и действительно изменил мое заявление, чтобы отразить ваши предложения;сказав, что проблема с этим изменением состоит в том, что оно требует переделки моей реализации класса и связанного с ней поведения способами, которых я бы предпочел избежать.

Моя проблема никогда не заключалась в передаче List (объекта) в мой веб-сервис (я просто поставил вопрос как таковой, чтобы упростить его для stackoverflow).В таком случае я был бы готов полностью согласиться с использованием универсального списка (из), но моя проблема была в том, что один из моих пользовательских объектов реализовал свойство со строго типизированным списком (из), например:

Customer {CustomerID AS Guid, FirstName AS String, LastName AS String, EmailAddresses AS EmailAddressList}

, который теперь нужно изменить на

Customer {CustomerID AS Guid, FirstName AS String, LastName AS String, EmailAddresses AS List(Of EmailAddress)} 

Это, конечно, не конец света и, вероятно, лучше в контексте веб-сервиса (как вы предложили), но определенно вред, когда речь идет овнутреннее использование приложения моих свойств Коллекции.Это означает, что, когда у меня есть это свойство, мне нужно либо приводить его в свой CustomCollectionList каждый раз, когда я хочу использовать какую-то расширенную функцию, либо мне нужно реализовать другое свойство, которое предоставляет CustomCollectionList.Любой раствор оставляет неприятный вкус во рту.

Ответы [ 5 ]

3 голосов
/ 08 января 2011

Прежде всего данные

{ PersonList: [ { FirstName: 'Max', LastName: 'Gershkovich' }, { FirstName: 'Test1', LastName: 'Test2' } ] }

, которые вы отправляете в веб-сервис, являются неверными данными JSON . Вы можете проверить данные на http://www.jsonlint.com/. Правильные данные JSON будут

{"PersonList":[{"FirstName":"Max","LastName":"Gershkovich"},{"FirstName":"Test1","LastName":"Test2"}]}

Более того, я рекомендую вам никогда не создавать JSON вручную . Вместо этого вы должны использовать JSON.stringify . В большинстве случаев функция даже изначально поддерживается в веб-браузерах (иногда после таких обновлений, как здесь ). Правильная сериализация параметров веб-сервиса может быть

$.ajax({
    data: {PersonList:JSON.stringify(t)} 
    // other parameters
});

(см. здесь для более подробной информации) или

$.ajax({
    data: JSON.stringify({ PersonList: t })
    // other parameters
});

, где

var t = [ { FirstName: 'Max', LastName: 'Gershkovich' },
          { FirstName: 'Test1', LastName: 'Test2' } ];

Какая версия представления данных JSON.stringify({ PersonList: t }) или {PersonList:JSON.stringify(t)} является правильной, зависит от других вещей. Легко проверить, какие из них работают в вашей среде.

Следующая небольшая проблема: лучше использовать List(Of Person) непосредственно в параметрах AddPersonList вместо использования типов PersonList, унаследованных от List(Of Person).

ОБНОВЛЕНО : Только сейчас я читаю ваши комментарии о List(Of Person) или PersonList к другому ответу. Чтобы не допустить такого же обсуждения, я решил написать свое мнение по этому поводу. Не важно, какие классы вы используете внутри вашего веб-сервиса. Вам следует разработать интерфейс веб-службы таким образом, чтобы он был простым и понятным для каждого, кто ничего не знает о вашей реализации. Входные данные метода (по крайней мере, что вы включили в вопрос) могут быть отлично описаны с помощью List(Of Person). Более того List(Of Person) это хорошо для передачи данных. Внутри реализации метода вы можете преобразовать List(Of Person) в любые другие классы, которые хороши для внутренних методов метода. Например, очень просто построить PersonList из List(Of Person). Поэтому я рекомендую вам действовать следующим образом: не допускать детали реализации интерфейса веб-службы.

ОБНОВЛЕНО 2 : Это не проблема, если объект, который является входным параметром веб-метода, имеет в качестве свойства другие объекты, такие как List(Of EmailAddress). Например, если вы определили EmailAddress из вашего последнего примера как

Public Class EmailAddress
    Public Property Type As String
    Public Property Address As String
End Class

и Customer как

Public Class Customer
    'Public CustomerID As Guid
    Public Property FirstName As String
    Public Property LastName As String
    Public Property EmailAddresses As List(Of EmailAddress)
End Class

тогда у вас не будет проблем с AddPersonList веб-методом, определенным следующим образом

<WebMethod()> _
Public Function AddPersonList(ByVal PersonList As List(Of Customer)) As String

Таким же образом вы можете использовать

<WebMethod()> _
Public Function AddPersonList1(ByVal input As InputData) As String

с

Public Class InputData
    Public Property PersonList As List(Of Customer)
End Class

или многие другие версии объекта для отправки информации на веб-сервер с использованием $.ajax.

На стороне клиента вы можете определить myData следующим образом

var myData = [
        { FirstName: 'Max', LastName: 'Gershkovich',
            EmailAddresses: [
                { Type: 'SMTP', Address: 'Max.Gershkovich@googlemail.com' },
                { Type: 'SMTP', Address: 'Max.Gershkovich@mail.ru' },
            ]
        },
        { FirstName: 'Test1', LastName: 'Test2' }
    ];

и затем отправьте данные на веб-сервер с помощью

$.ajax({
    type: "POST",
    url: "WebService1.asmx/AddPersonList",
    data: JSON.stringify({ PersonList: myData }),
    contentType: "application/json; charset=utf-8",
    dataType: "json",
    success: function (data) {
        alert(data.d);
    },
    error: function (xhr, textStatus, errorThrown) {
        alert("Error Occured!" + " | " + xhr.responseText + " | " +
              textStatus + " | " + errorThrown);
    }
});

или

$.ajax({
    type: "POST",
    url: "WebService1.asmx/AddPersonList1",
    data: JSON.stringify({ input: {PersonList: myData} }),
    contentType: "application/json; charset=utf-8",
    dataType: "json",
    success: function (data) {
        alert(data.d);
    },
    error: function (xhr, textStatus, errorThrown) {
        alert("Error Occured!" + " | " + xhr.responseText + " | " +
              textStatus + " | " + errorThrown);
    }
});

Все вышеперечисленное работает без проблем. Вы можете скачать тестовый пример , который делает это (должен быть запущен Default.htm. Чтобы вызвать метод, вы должны нажать на первую или вторую кнопку).

Если вы посмотрите на объект myData, определенный в коде клиента, а затем посмотрите на использование JSON.stringify выше, вы увидите, что у вас не возникнет проблем при отправке любых сложных объектов, включая массивы и свойства . На стороне сервера вы можете использовать List(Of Customer), List(Of EmailAddress) или список других объектов в качестве представлений массивов JavaScript. Все будет работать. Только ваш оригинальный пример с наследованием объектов - плохой.

Если вы попытаетесь спроектировать интерфейс веб-службы , глядя со стороны клиента, а не из внутренних серверных структур , вы легко создадите классы для соответствующих входных параметров веб-методов. И все сразу заработает. Если вам нужно будет инициализировать свои внутренние классы с информацией, вы сможете сделать это очень просто. Преобразование данных такого типа будет очень простым, и вам не нужно будет писать какой-либо CustomTypeResolver, который фактически делает то же самое.

1 голос
/ 10 января 2011

ОБНОВЛЕНИЕ: Первым шагом было бы перейти с .asmx на WCF .svc - это намного гибче, а его сериализатор намного умнее.

Обычно я просто использую общий списокаргумент, это меня пока не подвело.

Если вам нужно использовать собственное свойство ObjectList вместо универсального list<Object>, как насчет того, чтобы скрыть свойство ObjectList от сериализации и получить list<Object> прокси свойства между ними?

Таким образом сериализатор может сериализовать и десериализовать свойство list<Object>, в то время как вы можете продолжить работу со свойством ObjectList.

Это легко сделатьв WCF с помощью атрибута DataMember и с помощью DataMember(Name='ObjectList') вы даже можете продолжать использовать точно такие же имена свойств в js.

В целом вы получаете свойство, которое ведет себя как массив в javascript,и как ObjectList в .net.

0 голосов
/ 14 января 2011

Хотя ответ Олега кажется довольно полным и лучшим на данный момент, я хотел предложить альтернативный подход и рекомендовать библиотеку .NET NewtonSoft JSON .

Я согласен, что это хорошопусть ваши сервисные методы предоставляют четкое определение параметров, однако, когда я столкнулся с проблемами и мне нужно было что-то сделать как можно быстрее, принятие строки и анализ с этой библиотекой оказались полезными в крайнем случае.

0 голосов
/ 06 января 2011

Только предположите, есть ли пробел в вашей строке данных? Между "и {.

data: " { PersonList: [ { FirstName: 'Max', LastName: 'Gershkovich' }, { FirstName: 'Test1', LastName: 'Test2' } ] }",                 
       ^

Попробуйте убрать пространство. Любые изменения?

0 голосов
/ 06 января 2011

Ваши данные не соответствуют формату JSON.Пожалуйста, попробуйте использовать

data: " { PersonList: [ { 'FirstName': 'Max', 'LastName': 'Gershkovich' }, { 'FirstName': 'Test1', 'LastName': 'Test2' } ] }"

Для большей безопасности используйте библиотеку json для преобразования данных в json.

...