Permalänk

JSON Deserializing i C#/WP

Sitter och pular i Visual Studio 2010 och C# för windows phone, vilket jag har noll erfarenhet av sedan tidigare. Är framför allt webprogrammerare så är dålig på hela objekt-orienteringsbiten. Men för att komma till problemet, jag har alltså lyckats hämta json-data med en HttpWebRequest, och vill nu deserializa denna till ett objekt. Helst vill jag att objektet ska vara dynamiskt, så att jag inte behöver skriva en konstruktor för det i förväg.

Har dock lyckats med hjälp av biblioteket json.NET (http://json.codeplex.com/) och en konstruktor för mitt objekt att deserializa datan delvis, problemet är att min json innehåller sub-objekt:

json => hits => [0 => [w => "asdf", p => 2], 1 => [w => "gfdre", p=> 4] ], time => 0.43, num => 1, page => "search"]

Så bortser jag från sub-objektet "hits" fungerar det, men jag vill naturligtvis även få med detta. "hits" kan alltså innehålla n värden där varje värde har en string w och en int p.

Vad är bästa sättet att göra denna deserializing på i c# för windows phone?
Hittade detta http://blog.ronnieroller.com/json-serialization-on-windows-ph... men fick det ej att fungera, vet inte hur jag ska använda det, plus att jag får ett error på att T är undefined.

Här är min kod just nu:

public struct Results { public string Hits; public string Time; public int Num; public string Page; public Results(string hits,string time, int num, string page) { Hits = hits; Time = time; Num = num; Page = page; } } private void ReadCallback(IAsyncResult asynchronousResult) { HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState; HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult); using (StreamReader streamReader1 = new StreamReader(response.GetResponseStream())) { string resultString = streamReader1.ReadToEnd(); Results res = JsonConvert.DeserializeObject<Results>(resultString); } }

Det jag vill är att kunna hämta ett ord på detta sätt:
var myWord = deserialized["hits"][1]["w"];

Tack på förhand!

Permalänk
Medlem
Skrivet av infinity08:

Sitter och pular i Visual Studio 2010 och C# för windows phone, vilket jag har noll erfarenhet av sedan tidigare. Är framför allt webprogrammerare så är dålig på hela objekt-orienteringsbiten. Men för att komma till problemet, jag har alltså lyckats hämta json-data med en HttpWebRequest, och vill nu deserializa denna till ett objekt. Helst vill jag att objektet ska vara dynamiskt, så att jag inte behöver skriva en konstruktor för det i förväg.

Har dock lyckats med hjälp av biblioteket json.NET (http://json.codeplex.com/) och en konstruktor för mitt objekt att deserializa datan delvis, problemet är att min json innehåller sub-objekt:

json => hits => [0 => [w => "asdf", p => 2], 1 => [w => "gfdre", p=> 4] ], time => 0.43, num => 1, page => "search"]

Så bortser jag från sub-objektet "hits" fungerar det, men jag vill naturligtvis även få med detta. "hits" kan alltså innehålla n värden där varje värde har en string w och en int p.

Vad är bästa sättet att göra denna deserializing på i c# för windows phone?
Hittade detta http://blog.ronnieroller.com/json-serialization-on-windows-ph... men fick det ej att fungera, vet inte hur jag ska använda det, plus att jag får ett error på att T är undefined.

Här är min kod just nu:

public struct Results { public string Hits; public string Time; public int Num; public string Page; public Results(string hits,string time, int num, string page) { Hits = hits; Time = time; Num = num; Page = page; } } private void ReadCallback(IAsyncResult asynchronousResult) { HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState; HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult); using (StreamReader streamReader1 = new StreamReader(response.GetResponseStream())) { string resultString = streamReader1.ReadToEnd(); Results res = JsonConvert.DeserializeObject<Results>(resultString); } }

Det jag vill är att kunna hämta ett ord på detta sätt:
var myWord = deserialized["hits"][1]["w"];

Tack på förhand!

Jag har ingen direkt koll på hur detta fungerar så jag spånar fritt.

Jag vet inte hur han fick sin kod att fungera. Men på detta vis borde det fungera:

public static T Deserialize<T>(string json) { using (var ms = new System.IO.MemoryStream(Encoding.UTF8.GetBytes(json))) { var serializer = new DataContractJsonSerializer(typeof(T)); return (T)serializer.ReadObject(ms); } }

Används på samma sätt som du använder DeserlializeObject()-metoden
MinKlass mittObjekt = Deserialize<MinKlass>(jsonsträngensomskalldeserialiseras);

Det du har gjort borde fungera men jag tror att klassen behövs förändras för att allt ska bita, exempelvis listan med hits.

public class Result { public Hit[] hits; public string time; public int num; public string page; } public class Hit { public string w; public int p; }

Edit:
Jag hittade nu lite nyttig information om du inte redan har hittat det:
http://stackoverflow.com/questions/1212344/parse-json-in-c-sh...

Visa signatur

ηλί, ηλί, λαμά σαβαχθανί!?

Permalänk
Skrivet av Leedow:

Jag har ingen direkt koll på hur detta fungerar så jag spånar fritt.

Jag vet inte hur han fick sin kod att fungera. Men på detta vis borde det fungera:

Tackar för svar! Jag lyckades faktiskt få det att funka här efter att ha slitit mitt hår ytterligare ett par timmar. Jag löste det på följande sätt:

private void ReadCallback(IAsyncResult asynchronousResult) { HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState; HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult); using (StreamReader streamReader1 = new StreamReader(response.GetResponseStream())) { string resultString = streamReader1.ReadToEnd(); JObject res = (JObject)JsonConvert.DeserializeObject(resultString); var num = res["num"]; lblTitle.Dispatcher.BeginInvoke(() => lblTitle.Text = num + " träffar"); foreach (var resource in res["hits"]) { Debug.WriteLine("Word: " + (String)resource["w"]); lblTitle.Dispatcher.BeginInvoke(() => lbWords.Items.Add(new ItemViewModel() { LineOne = (String)resource["w"], LineTwo = (String)resource["p"] + " poäng", LineThree = "" }) ); } } }

Nu har jag dessvärre kört in i ett annat problem. Tanken var att alla träffarna skulle läggas in en lista (listbox lbWords). Tyvärr så blir alla items i listan likadana, för det mesta iallafall. Då och då kommer det in ett annat ord i den av någon anledning. Felet måste ligga i själva läggtill-funktionen och inte i mitt json-objekt eftersom Debug.WriteLine("Word: " + (String)resource["w"]); skriver ut rätt ord varje gång.

Nu vet jag inte exakt hur det här med BeginInvoke fungerar mer än att man modifierar en annan tråd, men kan det vara så att man måste lägga till alla items samtidigt genom att ändra ItemsSource på något sätt? Någon idé?

Ska väl tillägga att jag använde templaten "Windows Phone Databound Application" när jag skapade projektet, därav det där med LineOne och LineTwo.

Permalänk
Skrivet av infinity08:

Tackar för svar! Jag lyckades faktiskt få det att funka här efter att ha slitit mitt hår ytterligare ett par timmar. Jag löste det på följande sätt:

private void ReadCallback(IAsyncResult asynchronousResult) { HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState; HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult); using (StreamReader streamReader1 = new StreamReader(response.GetResponseStream())) { string resultString = streamReader1.ReadToEnd(); JObject res = (JObject)JsonConvert.DeserializeObject(resultString); var num = res["num"]; lblTitle.Dispatcher.BeginInvoke(() => lblTitle.Text = num + " träffar"); foreach (var resource in res["hits"]) { Debug.WriteLine("Word: " + (String)resource["w"]); lblTitle.Dispatcher.BeginInvoke(() => lbWords.Items.Add(new ItemViewModel() { LineOne = (String)resource["w"], LineTwo = (String)resource["p"] + " poäng", LineThree = "" }) ); } } }

Nu har jag dessvärre kört in i ett annat problem. Tanken var att alla träffarna skulle läggas in en lista (listbox lbWords). Tyvärr så blir alla items i listan likadana, för det mesta iallafall. Då och då kommer det in ett annat ord i den av någon anledning. Felet måste ligga i själva läggtill-funktionen och inte i mitt json-objekt eftersom Debug.WriteLine("Word: " + (String)resource["w"]); skriver ut rätt ord varje gång.

Nu vet jag inte exakt hur det här med BeginInvoke fungerar mer än att man modifierar en annan tråd, men kan det vara så att man måste lägga till alla items samtidigt genom att ändra ItemsSource på något sätt? Någon idé?

Ska väl tillägga att jag använde templaten "Windows Phone Databound Application" när jag skapade projektet, därav det där med LineOne och LineTwo.

Ta en titt på det här projektet. Det Magiska ligger i MainPage.xaml samt "MinListBox.ItemsSource = _ettTest._DetHarBinderJagTill;"
Härifrån bör du kunna applicera det här på din egna kod.

https://bambafile.com/Download/Item/010412d2-9555-410d-929f-6...

Permalänk
Skrivet av shakeshar:

Ta en titt på det här projektet. Det Magiska ligger i MainPage.xaml samt "MinListBox.ItemsSource = _ettTest._DetHarBinderJagTill;"
Härifrån bör du kunna applicera det här på din egna kod.

https://bambafile.com/Download/Item/010412d2-9555-410d-929f-6...

Tackar! Har suttit och labbat ett tag med din kod nu men jag lyckas inte skicka in ett objekt som jag sedan kan loopa över. Jag har alltså ett objekt omvandlat från JSON (se ovan), och jag vill att varje item i objektet ska läggas in som en post i listboxen. Körde följande test med din kod, jag kan utan problem skicka in en sträng, men så fort jag gör om det till ett object får jag error:

public partial class MainPage : PhoneApplicationPage { public MainPage() { InitializeComponent(); } private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e) { TestData _ettTest = new TestData(); object[] o = new Object[5]; o[0] = "Namn1"; o[1] = "Namn2"; _ettTest.Fornamn = o; _ettTest.LaddaTestData(); MinListBox.ItemsSource = _ettTest._DetHarBinderJagTill; } private void MinListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (sender is ListBox) { if ((sender as ListBox).SelectedItem is TestData) { TestData _denHarHarJagValt = (sender as ListBox).SelectedItem as TestData; } } } } public class TestData { public ObservableCollection<TestData> _DetHarBinderJagTill = new ObservableCollection<TestData>(); private object[] f; public object[] Fornamn { get { return f; } set { f = value; } } public string Efternamn { get; set; } public int Alder { get; set; } public void LaddaTestData() { _DetHarBinderJagTill.Add(new TestData { Alder = 10, Efternamn = "Andersson", Fornamn = Fornamn[0] }); _DetHarBinderJagTill.Add(new TestData { Alder = 10, Efternamn = "Andersson", Fornamn = Fornamn[1] }); } }

Vad gör jag för fel?

Permalänk
Skrivet av infinity08:

Tackar! Har suttit och labbat ett tag med din kod nu men jag lyckas inte skicka in ett objekt som jag sedan kan loopa över. Jag har alltså ett objekt omvandlat från JSON (se ovan), och jag vill att varje item i objektet ska läggas in som en post i listboxen. Körde följande test med din kod, jag kan utan problem skicka in en sträng, men så fort jag gör om det till ett object får jag error:

public partial class MainPage : PhoneApplicationPage { public MainPage() { InitializeComponent(); } private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e) { TestData _ettTest = new TestData(); object[] o = new Object[5]; o[0] = "Namn1"; o[1] = "Namn2"; _ettTest.Fornamn = o; _ettTest.LaddaTestData(); MinListBox.ItemsSource = _ettTest._DetHarBinderJagTill; } private void MinListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (sender is ListBox) { if ((sender as ListBox).SelectedItem is TestData) { TestData _denHarHarJagValt = (sender as ListBox).SelectedItem as TestData; } } } } public class TestData { public ObservableCollection<TestData> _DetHarBinderJagTill = new ObservableCollection<TestData>(); private object[] f; public object[] Fornamn { get { return f; } set { f = value; } } public string Efternamn { get; set; } public int Alder { get; set; } public void LaddaTestData() { _DetHarBinderJagTill.Add(new TestData { Alder = 10, Efternamn = "Andersson", Fornamn = Fornamn[0] }); _DetHarBinderJagTill.Add(new TestData { Alder = 10, Efternamn = "Andersson", Fornamn = Fornamn[1] }); } }

Vad gör jag för fel?

Det här gör du fel:

object[] o = new Object[5]; o[0] = "Namn1"; o[1] = "Namn2"; _ettTest.Fornamn = o;

Fel 1: Skippa object, du vet ju vad du har, string string string.

Om du kollar mitt exemple så binder du mot klassen "TestData" där du har ett listobjekt för varje rad i listan. Släpp Objekt när du vet vad du jobbar med.

_ettTest.Fornamn kan bara innehåll ett namn, nu trycker du in Alla namn på en och samma och sen försöker du binda det till O, vilket inte finns i klassen. Så namen är jätteviktiga här.

Permalänk
Skrivet av shakeshar:

Det här gör du fel:

object[] o = new Object[5]; o[0] = "Namn1"; o[1] = "Namn2"; _ettTest.Fornamn = o;

Fel 1: Skippa object, du vet ju vad du har, string string string.

Om du kollar mitt exemple så binder du mot klassen "TestData" där du har ett listobjekt för varje rad i listan. Släpp Objekt när du vet vad du jobbar med.

_ettTest.Fornamn kan bara innehåll ett namn, nu trycker du in Alla namn på en och samma och sen försöker du binda det till O, vilket inte finns i klassen. Så namen är jätteviktiga här.

Jag förstår inte riktigt hur du menar. Är inte van att arbeta med klasser överhuvudtaget, så det är nytt tänkande för mig. I ditt exempel var ju datan hårdkodad i klassen, men jag måste få in en okänd mängd strängar dynamiskt under runtime.

Ponera att jag har ett objekt o som innehåller n items, det är mitt utgångsläge:
o[0] = "abc"
o[1] = "cde"
o[2] = "efg"
o[n] = string
...

hur ska jag kunna anropa klassen med detta objekt?

För att komplicera detta ytterligare sedan så är det dessutom inte strängar som ska in, utan sub-objekt. Detta eftersom jag vill att varje rad i listan ska ha en titel och tillhörande beskrivning. Så i mitt fall är objektet följande:
o[0]["w"] = "text";
o[0]["p"] = "beskrivning";
o[1]["w"] = "text 2";
o[1]["p"] = "beskrivning 2";
...

Permalänk
Skrivet av infinity08:

Jag förstår inte riktigt hur du menar. Är inte van att arbeta med klasser överhuvudtaget, så det är nytt tänkande för mig. I ditt exempel var ju datan hårdkodad i klassen, men jag måste få in en okänd mängd strängar dynamiskt under runtime.

Ponera att jag har ett objekt o som innehåller n items, det är mitt utgångsläge:
o[0] = "abc"
o[1] = "cde"
o[2] = "efg"
o[n] = string
...

hur ska jag kunna anropa klassen med detta objekt?

För att komplicera detta ytterligare sedan så är det dessutom inte strängar som ska in, utan sub-objekt. Detta eftersom jag vill att varje rad i listan ska ha en titel och tillhörande beskrivning. Så i mitt fall är objektet följande:
o[0]["w"] = "text";
o[0]["p"] = "beskrivning";
o[1]["w"] = "text 2";
o[1]["p"] = "beskrivning 2";
...

Jag uppdaterade projektet jag skickade tidigare. Blir det här tydligare?
EDIT:
Missade sub-biten, jag återkommer med uppdaterat projekt

Permalänk
Skrivet av infinity08:

Jag förstår inte riktigt hur du menar. Är inte van att arbeta med klasser överhuvudtaget, så det är nytt tänkande för mig. I ditt exempel var ju datan hårdkodad i klassen, men jag måste få in en okänd mängd strängar dynamiskt under runtime.

Ponera att jag har ett objekt o som innehåller n items, det är mitt utgångsläge:
o[0] = "abc"
o[1] = "cde"
o[2] = "efg"
o[n] = string
...

hur ska jag kunna anropa klassen med detta objekt?

För att komplicera detta ytterligare sedan så är det dessutom inte strängar som ska in, utan sub-objekt. Detta eftersom jag vill att varje rad i listan ska ha en titel och tillhörande beskrivning. Så i mitt fall är objektet följande:
o[0]["w"] = "text";
o[0]["p"] = "beskrivning";
o[1]["w"] = "text 2";
o[1]["p"] = "beskrivning 2";
...

Prova den här, den länken nedan. Är osäker på om det där kommer gå igenom, det skulle underlätta om jag hade json urlen.
https://bambafile.com/Download/Item/73e1b66e-d6e0-4787-bf95-6...

Permalänk
Skrivet av shakeshar:

Prova den här, den länken nedan. Är osäker på om det där kommer gå igenom, det skulle underlätta om jag hade json urlen.
https://bambafile.com/Download/Item/73e1b66e-d6e0-4787-bf95-6...

Strålande, nu funkar det ypperligt! Nu ska jag bara lyckas ordna ett developerkonto så att jag kan testköra min app på telefonen

En fråga dock, av ren nyfikenhet. Varför använder man getters och setters i klassen när de bara används för att avgöra vilken rad i listboxen texten ska hamna på? Är lite förvirrad över hela get-set-grejen, jag trodde det var ett sätt att anropa klassen med data. Nu visade du ju att man kunde skicka in datan direkt som ett argument i metoden istället, det gör att getters och setters känns överflödiga.

Förstår inte riktigt deras funktion här:

public class ListItemSource { public ObservableCollection<ListItemSource> myCollection = new ObservableCollection<ListItemSource>(); public string LineOne { get; set; } public string LineTwo { get; set; } public int LineThree { get; set; } public void LoadData(JObject res) { for (int i = 0; i < res["hits"].Count(); i++) { var _currentObjekt = res["hits"][i]; myCollection.Add(new ListItemSource { LineOne = (string)_currentObjekt["w"], LineTwo = _currentObjekt["p"] + " poäng" }); } } }

En annan grej jag har funderat på är det här med upplösningar på olika enheter. När jag kodade för android var jag alltid noga med att sätta värden i procent istället för pixlar så att det ska se bra ut på alla upplösningar och skärmstorlekar. Det har jag dock inte gjort nu, titeln och själva listan kom ju som standard när jag skapade projektet och man får väl anta att det är rätt då? Kommer det se bra ut i olika upplösningar? Går det att testa på något sätt?

Edit: Jämförde nu propertyn "width" på autogenererade labeln med en knapp jag har. Labeln har width:auto, min knapp 436. Betyder det att min knapp kommer se fruktansvärd ut om man inte råkar ha just den upplösningen? Sätter jag width till auto på den blir den liten och högerjusterad, jag vill ju att den ska täcka hela ytan från vänster till höger på skärmen minus lite marginal..