Avatar billede Greenland Nybegynder
24. marts 2011 - 13:04 Der er 8 kommentarer og
1 løsning

json og serialize i .net uden at definere class til objektet

Hej,

Jeg har behov for at kalde json fra min .net kode. Jeg forsøger med følgende helper class:
public class JSONHelper
    {
        public static string Serialize<T>(T obj)
        {
            System.Runtime.Serialization.Json.DataContractJsonSerializer serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(obj.GetType());
            MemoryStream ms = new MemoryStream();
            serializer.WriteObject(ms, obj);
            string retVal = Encoding.Default.GetString(ms.ToArray());
            ms.Dispose();
            return retVal;
        }

        public static T Deserialize<T>(string json)
        {
            T obj = Activator.CreateInstance<T>();
            MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json));
            System.Runtime.Serialization.Json.DataContractJsonSerializer serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(obj.GetType());
            obj = (T)serializer.ReadObject(ms);
            ms.Close();
            ms.Dispose();
            return obj;
        }
    }


Yderligere har jeg følgende klasse:
[DataContract]
    public class Person
    {
        public Person() { }
        public Person(string firstName, string lastName)
        {
            this.FirstName = firstName;
            this.LastName = lastName;
        }

       
        [DataMember]
        public string FirstName { get; set; }

        [DataMember]
        public string LastName { get; set; }

       
    }

... og jeg anvender det således i min pageload:

Person myPerson = new Person("Peter", "Pan");
     
        // Serialize
        string json = JSONHelper.Serialize<Person>(myPerson);
       
       
        // Deserialize
        myPerson = JSONHelper.Deserialize<Person>(json);

og jeg kan så skrive:
Response.Write(myPerson.FirstName)  og det heler funker som det skal.

Men når jeg kalder min url der returnerer mig et jsonobject, så får jeg null i mine attributter. dvs. at myPerson.FirstName er null

Det skal siges at objektet jeg får retur ikke er ens med min Person klasse. Og ja jeg ved godt den burde være det, men hvis man kalder samme url i javascript eller php så behøves man ikke at "kende" sit objekt til bevidstløshed.

Blot man viser til de rigtige feltnavne som her:
java script:
  var rese = eval('(' + res + ')');
  document.write(rese.firstName + "<br>" + rese.firstName);


eller i php:

  $firstName  = $data->firstName;
  $lastName  = $data->lastName;

Så mit spørgsmål går på følgende:

Hvordan kan jeg arbejde med mit returnerede json objekt uden at skulle definere en class inde i min kode ?
Hvis der fx tilføjes et ekstra feltnavn til json objektet, så vil jeg at min kode skal være upåvirket af det, fordi det kan godt være at jeg ikke har behov for feltet, men kun aflæser firstname og lastname !


Håber at spørgsmålet er forståeligt, og håber at få noget response på dette ?

mvh
Greenland
Avatar billede arne_v Ekspert
25. marts 2011 - 00:51 #1
Det er lidt vanskeligere i C# end i PHP fordi C# er et typestærkt sprog.

Men se nedenfor for et forslag til løsning:

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Web.Script.Serialization;
 
namespace E
{
    public static class Json1
    {
        public static string Serialize<T>(T o)
        {
            DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
            MemoryStream ms = new MemoryStream();
            ser.WriteObject(ms, o);
            return Encoding.UTF8.GetString(ms.ToArray());
        }
        public static T Deserialize<T>(string json)
        {
            MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json));
            DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
            return (T)ser.ReadObject(ms);
        }
    }
    [DataContract]
    public class Person
    {
        public Person() : this("", "")
        {
        }
        public Person(string firstName, string lastName)
        {
            this.FirstName = firstName;
            this.LastName = lastName;
        }
        [DataMember]
        public string FirstName { get; set; }
        [DataMember]
        public string LastName { get; set; }
    }   
    public static class Json2
    {
        public static string Serialize(object o)
        {
            JavaScriptSerializer ser = new JavaScriptSerializer();
            return ser.Serialize(o);
        }
        public static T Deserialize<T>(string json)
        {
            JavaScriptSerializer ser = new JavaScriptSerializer();
            return ser.Deserialize<T>(json);
        }
        public static object Deserialize(string json)
        {
            JavaScriptSerializer ser = new JavaScriptSerializer();
            return ser.DeserializeObject(json);
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            Person o = new Person("Peter", "Pan");
            string json = Json1.Serialize<Person>(o);
            Console.WriteLine(json);
            Person ox = Json1.Deserialize<Person>(json);
            Console.WriteLine(ox.FirstName + " " + ox.LastName);
            Person o2 = new Person("Peter", "Pan");
            string json2 = Json2.Serialize(o2);
            Console.WriteLine(json2);
            Person o2x = Json2.Deserialize<Person>(json2);
            Console.WriteLine(o2x.FirstName + " " + o2x.LastName);
            IDictionary<string, object> o2xx = (IDictionary<string, object>)Json2.Deserialize(json2);
            Console.WriteLine(o2xx["FirstName"] + " " + o2xx["LastName"]);
            string json3 = "{\"FN\":\"Peter\",\"LN\":\"Pan\"}";
            Console.WriteLine(json2);
            IDictionary<string, object> o3xx = (IDictionary<string, object>)Json2.Deserialize(json3);
            Console.WriteLine(o3xx["FN"] + " " + o3xx["LN"]);
            Console.ReadKey();
        }
    }
}
Avatar billede Greenland Nybegynder
25. marts 2011 - 12:43 #2
Hej Arne,

Wow det er flot, og virker perfekt

Tak for hurtigt og elegant svar


Jeg har dog et lille "issue" vedr. hvordan jeg kan aflæse street i address i følgende json streng:
"{\n    \"vaerdi\" : 20,\n    \"laengde\" : 0\n        , \"ialt\": 47\t\n        ,\"address\": [\n        \n            { \n            \t\t\t\"id\": \"29\",\n            \t\t\t\"grp_id\": \"NGL13510658\",\n\t\t\t\t\"street\": \"storegade\"


Den kode jeg anvendte fra dig er:
IDictionary<string, object> o3xx = (IDictionary<string, object>)E.Json2.Deserialize(json3);

Response.Write("Værdi: " + o3xx["vaerdi"] + " længde: " +
o3xx["laengde"] + "Ialt: " + o3xx["ialt"] +
" contacts and address: " + o3xx["address"] + "<p>");

jeg har forsøgt at skrive o3xx["address.street"], men den går inte !??

nb:
Jeg har også behov for at loope alle objekter i adress, så vis du har en ide til det er du mer end velkommen at smide et eksempel

På forhånd tak

vh
greenland
Avatar billede arne_v Ekspert
25. marts 2011 - 15:59 #3
Eksempel paa nesting:

using System;
using System.Collections.Generic;
using System.Web.Script.Serialization;

namespace E
{
    public static class Json
    {
        public static object Deserialize(string json)
        {
            JavaScriptSerializer ser = new JavaScriptSerializer();
            return ser.DeserializeObject(json);
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            string json = "{\"a\":\"This is a\",\"b\":{\"c\":\"This is c\",\"d\":\"This is d\"}}";
            Console.WriteLine(json);
            IDictionary<string, object> o = (IDictionary<string, object>)Json.Deserialize(json);
            string a = (string)o["a"];
            IDictionary<string, object> b = (IDictionary<string, object>)o["b"];
            string c = (string)b["c"];
            string d = (string)b["d"];
            Console.WriteLine(a + " " + c + " " + d);
            Console.ReadKey();
        }
    }
}
Avatar billede arne_v Ekspert
25. marts 2011 - 16:12 #4
Eksempel paa mere dynamisk tilgang:

using System;
using System.Collections.Generic;
using System.IO;
using System.Web.Script.Serialization;

namespace E
{
    public static class Json
    {
        public static object Deserialize(string json)
        {
            JavaScriptSerializer ser = new JavaScriptSerializer();
            return ser.DeserializeObject(json);
        }
        private static void Dump(IDictionary<string, object> o, TextWriter tw, string ind)
        {
            foreach(KeyValuePair<string, object> kvp in o)
            {
                tw.Write(ind + kvp.Key + ": ");
                if(kvp.Value is string)
                {
                    tw.WriteLine((string)kvp.Value);
                }
                else if (kvp.Value is IDictionary<string, object>)
                {
                    tw.WriteLine();
                    Dump((IDictionary<string, object>)kvp.Value, tw, ind + "  ");
                }
                else
                {
                    throw new Exception(kvp.Value.GetType().FullName + " not supported yet");
                }
            }
        }
        public static void Dump(IDictionary<string, object>
                                o, TextWriter tw)
        {
            Dump(o, tw, "");
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            string json = "{\"a\":\"This is a\",\"b\":{\"c\":\"This is c\",\"d\":\"This is d\"}}";
            Console.WriteLine(json);
            IDictionary<string, object> o = (IDictionary<string, object>)Json.Deserialize(json);
            Json.Dump(o, Console.Out);
            Console.ReadKey();
        }
    }
}
Avatar billede arne_v Ekspert
25. marts 2011 - 16:13 #5
og et svar
Avatar billede Pulchra Nybegynder
28. marts 2011 - 23:24 #6
Her er en anden mulig løsning, som baserer sig på typen dynamic. Så den virker kun i .Net 4.0.

Vi kan starte med at se på hvordan den kan bruges til det eksempel json du postede.

    string json =
        "{\"vaerdi\":20,\"laengde\":0,\"ialt\":47,\"" +
        "address\":[{ \"id\":\"29\",\"grp_id\":\"NGL13510658\",\"street\":\"storegade\"}]} ";

    var serializer = new JavaScriptSerializer();
    serializer.RegisterConverters(new[] {new DynamicJsonConverter()});
    dynamic obj = serializer.Deserialize(json, typeof (object));

    int value = obj.vaerdi;
    int length = obj.laengde;
    int total = obj.ialt;
    string id = obj.address[0].id;
    string street = obj.address[0].street;
    //osv.

Som du kan se, så er det næsten som i javascript, da det er muligt at tilgå dataene med standard dot notation.

Men her er hele koden. Den har jeg på et eller andet tidspunkt lånt fra stackoverflow.com.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Dynamic;
using System.Linq;
using System.Web.Script.Serialization; //ligger i System.Web.Extensions

namespace Dynamic
{
    internal sealed class DynamicJsonConverter : JavaScriptConverter
    {
        public override IEnumerable<Type> SupportedTypes
        {
            get { return new ReadOnlyCollection<Type>(new List<Type>(new[] {typeof (object)})); }
        }

        public override object Deserialize(IDictionary<string, object> dictionary, Type type,
                                          JavaScriptSerializer serializer)
        {
            if (dictionary == null)
            {
                throw new ArgumentNullException("dictionary");
            }
            return type == typeof (object) ? new DynamicJsonObject(dictionary) : null;
        }

        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }

    internal sealed class DynamicJsonObject : DynamicObject
    {
        private readonly IDictionary<string, object> _dictionary;

        public DynamicJsonObject(IDictionary<string, object> dictionary)
        {
            if (dictionary == null)
            {
                throw new ArgumentNullException("dictionary");
            }
            _dictionary = dictionary;
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            if (!_dictionary.TryGetValue(binder.Name, out result))
            {
                // return null to avoid exception.  caller can check for null this way...       
                result = null;
                return true;
            }
            var dictionary = result as IDictionary<string, object>;
            if (dictionary != null)
            {
                result = new DynamicJsonObject(dictionary);
                return true;
            }
            var arrayList = result as ArrayList;
            if (arrayList != null && arrayList.Count > 0)
            {
                if (arrayList[0] is IDictionary<string, object>)
                {
                    result =
                        new List<object>(
                            arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)));
                }
                else
                {
                    result = new List<object>(arrayList.Cast<object>());
                }
            }
            return true;
        }
    }
}

Mvh.
Hallur
Avatar billede Greenland Nybegynder
05. april 2011 - 17:20 #7
Tak for svar Arne,

Jeg har dog stadig problemer med at fá fat på address, og får følgende fejl:

Unable to cast object of type 'System.Object[]' to type 'System.Collections.Generic.IDictionary`2[System.String,System.Object]'.

IDictionary<string, object> o3xx = (IDictionary<string, object>)t.Json.Deserialize(json3);
        int vaerdi = (int)o3xx["vaerdi"];
        string tmp2 = "";
        IDictionary<string, object> bb = (IDictionary<string, object>)o3xx["address"];
       
Jeg får fejlen i sidste linje vedr address !:-(

mvh
Greenland
nb: Hallur, det ser spændende ud med typen dynamic.. jeg må heller opgradere
Avatar billede arne_v Ekspert
09. april 2011 - 02:52 #8
Den fejl skyldes at address er et array og ikke et objekt.
Avatar billede arne_v Ekspert
09. april 2011 - 03:00 #9
array returneres som object[] ikek som IDictionary<string.object>

demo:

using System;
using System.Collections.Generic;
using System.Web.Script.Serialization;

namespace E
{
    public static class Json
    {
        public static object Deserialize(string json)
        {
            JavaScriptSerializer ser = new JavaScriptSerializer();
            return ser.DeserializeObject(json);
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            string json = "{\"a\":\"This is a\",\"b\":{\"c\":\"This is c\",\"d\":\"This is d\"},\"e\":[1,2,3]}";
            Console.WriteLine(json);
            IDictionary<string, object> o = (IDictionary<string, object>)Json.Deserialize(json);
            string a = (string)o["a"];
            IDictionary<string, object> b = (IDictionary<string, object>)o["b"];
            string c = (string)b["c"];
            string d = (string)b["d"];
            Console.WriteLine(a + " " + c + " " + d);
            object[] e = (object[])o["e"];
            int e0 = (int)e[0];
            int e1 = (int)e[1];
            int e2 = (int)e[2];
            Console.WriteLine(e0 + " " + e1 + " " + e2);
            Console.ReadKey();
        }
    }
}
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester