2011-01-06 1 views
9

Così ho elaborato come passare i miei oggetti personalizzati nei servizi web di ASP.Net json. Funziona un fascino.Risoluzione dei tipi di array JSON nei servizi Web ASP.Net

Problema che sto avendo sta passando in matrici diritte dei miei oggetti personalizzati o alternativamente passando in array che sono parametri dei miei oggetti personalizzati.

Così, per esempio ...

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> 

esito errore: Il valore \ "System.Collections.Generic.Dictionary`2 [System.String, System.Object] \" non è di tipo \ "WebApplication1.WebService1 + Person \" e non può essere utilizzato in questa raccolta generica. \ R \ nParametro nome: valore

Quindi, come posso spiegare a ASP.net che si tratta di una serie di persone?

Nota: il fatto di modificare la funzione come Elenco (di Persona) o ArrayList funziona, ma dato che implemento le mie raccolte personalizzate questo non è ottimale per me.

UPDATE: Ok, quindi, quello che ho elaborato finora è che questo problema è sicuramente associato a come JavascriptSerializer utilizza SimpleTypeResolver per risolvere i tipi. In pratica se faccio qualcosa di simile

Public Function AddPersonList(ByVal PersonList As String) As PersonList 

posso ricreare l'errore utilizzando il seguente codice.

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

Tuttavia quando fornisco il mio tipo personalizzato resolver lungo le linee di

Dim jsonSerializer As New JavaScriptSerializer(New MyCustomTypeResolver) 

posso creare con successo un caso del mio elenco personalizzato.

Ora ho elaborato come fornire il mio convertitore personalizzato nel file web.config. Sulla falsariga di questo ...

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

Ma il problema è che non riesco a trovare un modo per specificare un CustomTypeResolver. La mia ipotesi è che questo è l'unico pezzo del puzzle che mi manca.

PSS: ho cercato di utilizzare l'assembly nome completo come specificato nella documentazione per il SimpleTypeResolver (SimpleTypeResolver MSDN) ma questo genera un "Operazione non è valida a causa dello stato corrente dell'oggetto." eccezione - che è un errore causato quando il TypeResolver non può risolvere il nome (so che questo testando con il mio CustomTypeResolver)

UPDATE @ Oleg e Sean:

Apprezzo sinceramente il vostro input e hanno infatti cambiato la mia domanda per riflettere i tuoi suggerimenti; avendo detto che il problema con questo cambiamento è che richiede una rielaborazione della mia implementazione di classe e del suo comportamento associato in modi che avrei preferito evitare.

Il mio problema non è mai stato passare in una lista (di oggetto) nel mio servizio web (ho semplicemente posto la domanda in quanto tale per semplificarlo per StackOverflow). In tal caso sarei disposto ad accettare completamente con un elenco generico (di), ma il mio problema era in realtà che uno dei miei oggetti personalizzati implementato una proprietà con un forte tipizzato lista (dei) così per esempio:

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

che ora ha bisogno di essere cambiato in

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

Questa non è certamente la fine del mondo ed è probabilmente meglio nel contesto webservice (come avete suggerito), ma sicuramente un danno quando si tratta di utilizzo applicazione interna delle le mie proprietà della collezione. Ciò significa che una volta che ho questa proprietà ho bisogno di trasmetterla alla mia CustomCollectionList ogni volta che voglio usare alcune funzionalità avanzate o ho bisogno di implementare un'altra proprietà che espone la CustomCollectionList. Entrambe le soluzioni lasciano un sapore sgradevole nella mia bocca.

+0

Per quanto mi riguarda, si tratta di una limitazione del meccanismo di serializzazione in .NET per JSON. Devo ancora vedere un esempio di questa funzionalità e non sono stato in grado di risolverlo da solo. Basta rileggere la domanda dopo un upvote –

risposta

0

I dati sembrano non essere in linea con il formato JSON. Prova a utilizzare

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

Per essere più sicuri, utilizzare la libreria json per convertire i dati in json.

+0

Provato ma lo stesso problema. Sembra analizzare il JSON senza problemi. A giudicare dal messaggio di errore questo mi sembra una specie di errore di risoluzione del tipo che non so come mappare correttamente la matrice al mio oggetto lista. –

+0

Provato e funziona bene per me. Hai incluso l'attributo sulla classe webservice? – Ashish

+0

Sì, ho usato lo stesso identico codice? La mia ipotesi è che tu abbia usato qualcosa come Public Function AddPersonList (ByVal PersonList As ArrayList) As String che funziona bene anche per me ??? –

0

Solo un'ipotesi, c'è uno spazio principale nella stringa di dati? Tra "e {.

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

Prova a prendere lo spazio fuori. Ogni cambiamento?

+0

C'era .... Provato, ancora non va bene :-(stesso errore Sembra che avrò bisogno di registrare un convertitore personalizzato e fare qualcosa su quelle linee –

+0

Spazio non sarà un problema. commento per la possibile soluzione – Ashish

3

Prima di tutto i dati di

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

che si invia al servizio web sono dati JSON sbagliate È possibile verificare i dati su http://www.jsonlint.com/. I dati JSON corretti saranno

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

Inoltre, si consiglia di non eseguire manualmente il JSON . Invece di quello dovresti usare JSON.stringify. Nella maggior parte dei casi la funzione è pari a natively supported nei browser Web (a volte dopo aggiornamenti come here). serializzazione corretta dei parametri di servizio Web può essere

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

(vedi here per maggiori dettagli) o

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

dove

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

Quale versione di rappresentazione dei dati JSON.stringify({ PersonList: t }) o {PersonList:JSON.stringify(t)} è corretto dipendono altre cose. È facile testare quali lavorano nel tuo ambiente.

Successivo piccolo problema: si dovrebbe meglio utilizzare List(Of Person) direttamente nei parametri di AddPersonList invece di l'utilizzo di tipi PersonList ereditato da List(Of Person).

AGGIORNATO: Poco fa ho letto i vostri commenti su List(Of Person) o PersonList ad un'altra risposta. Per evitare la stessa discussione decido di scrivere la mia opinione al riguardo. Non è importante quali classi utilizzi all'interno del tuo servizio web. Dovresti progettare l'interfaccia del servizio web in modo che sia semplice e chiaro per ogni persona che non sa nulla della tua implementazione. I dati di input del metodo (almeno quello che hai incluso nella domanda) possono essere perfettamente descritti con List(Of Person). Inoltre List(Of Person) è buono per il trasferimento dei dati. All'interno dell'implementazione del metodo è possibile convertire List(Of Person) in qualsiasi altra classe che sia valida per i metodi interni. Ad esempio, è molto semplice creare PersonList da List(Of Person). Quindi ti consiglio di seguire la procedura: mantieni l'interfaccia del servizio web libera dai dettagli di implementazione.

AGGIORNAMENTO 2: Non è un problema se l'oggetto, che è il parametro di input del metodo web, ha come la proprietà altri oggetti come List(Of EmailAddress). Ad esempio, se si definisce EmailAddress dal tuo ultimo esempio come

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

e Customer come

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 

allora si avrà alcun problema con AddPersonList metodo Web definito come segue

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

In allo stesso modo puoi usare

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

con

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

o di molte altre versioni di oggetto per inviare le informazioni al server Web utilizzando $.ajax.

Sul lato client è possibile definire myData come segue

var myData = [ 
     { FirstName: 'Max', LastName: 'Gershkovich', 
      EmailAddresses: [ 
       { Type: 'SMTP', Address: '[email protected]' }, 
       { Type: 'SMTP', Address: '[email protected]' }, 
      ] 
     }, 
     { FirstName: 'Test1', LastName: 'Test2' } 
    ]; 

e poi inviare i dati al server web con

$.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); 
    } 
}); 

o

$.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); 
    } 
}); 

Tutto il lavoro di cui sopra senza alcun problema. È possibile scaricare the test example, che esegue questa operazione (deve essere avviato Default.htm. Per chiamare il metodo, è necessario fare clic sul primo o sul secondo pulsante).

Se si guarda l'oggetto myData definito nel codice client e poi guardare l'utilizzo di JSON.stringify sopra vedrete che avrete nessun problema a mandare eventuali oggetti complessi tra cui gli array e le proprietà. Sul lato server è possibile utilizzare List(Of Customer), List(Of EmailAddress) o un elenco di altri oggetti come rappresentazioni di Array JavaScript. Tutto funzionerà. Solo il tuo esempio originale con l'ereditarietà degli oggetti è cattivo.

Se si tenta di progettare l'interfaccia del servizio Web guardando dal lato client e non dalle strutture interne del server, sarà facile costruire le classi per i parametri di input corrispondenti dei metodi Web. E tutto funzionerà immediatamente. Se hai bisogno di inizializzare le tue classi interne con le informazioni, sarai in grado di farlo molto semplicemente. Questo tipo di conversione dei dati sarà molto semplice e non sarà necessario scrivere alcun CustomTypeResolver che faccia effettivamente lo stesso.

+0

Si prega di consultare l'aggiornamento –

1

UPDATE: Il primo passo sarebbe passare da .asmx a .svc di WCF: è molto più flessibile e il suo serializzatore è molto più intelligente.

Normalmente uso solo un argomento di lista generico, questo non mi ha deluso fino ad ora.

Se è necessario utilizzare una proprietà personalizzata ObjectList invece di un generico list<Object>, come di nascondere la proprietà ObjectList dalla serializzazione e hanno un proxy list<Object> proprietà tra i due?

In questo modo il serializzatore può serializzare e deserializzare la proprietà list<Object>, mentre si può continuare a lavorare con la proprietà ObjectList.

Questo è facilmente possibile in WCF utilizzando l'attributo DataMember e utilizzando DataMember(Name='ObjectList') si può anche continuare a utilizzare gli stessi identici nomi di proprietà in js.

Tutto sommato si ottiene una proprietà che si comporta come un array in javascript e come ObjectList in .net.

+0

Si prega di consultare il mio aggiornamento –

+0

E vedere il mio :) –

+0

Scusa, basta rileggere la tua idea (lo so male dopo tutto questo tempo), ma non è poi così male.Potrebbe contrassegnare la proprietà NotSerializable (o qualunque sia l'attributo). –

0

Mentre la risposta di Oleg sembra piuttosto completa e la migliore finora, volevo fornire un approccio alternativo e raccomandare .NET NewtonSoft JSON Library.

Sono d'accordo sul fatto che sia opportuno che i propri metodi di servizio forniscano una definizione chiara dei parametri, tuttavia, quando ho incontrato problemi e ho bisogno di ottenere qualcosa funzionante al più presto, accettare una stringa e analizzare con questa libreria ha dimostrato utile in pizzico.