2

I am new to windows phone 8 development...How to parse the following data in windows phone 8 :

   [
   {
      "detail":{
         "single_selection":[
            {
               "Questions":"If  -2<1\/2x< 4 ,  then",
               "Question Type":"text",
               "Url":"NO URL",
               "options":{
                  "2379":"4 > x < -8",
                  "2380":"4 < x > -8",
                  "2381":"4 < x < -8",
                  "2382":"4 > x > -8"
               },
               "correct Ans":[
                  "2382"
               ],
               "marks":"1",
               "description":"4 > x > -8"
            }
         ]
      }
   }
]

I am trying it to parse in the following way:

 namespace TestSample
{
    public partial class MainPage : PhoneApplicationPage
    {
        private const string Con_String = @"isostore:/DataBase.sdf";
        // Constructor
        public MainPage()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(MainPage_Loaded);         


        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            WebClient webClient = new WebClient();
            webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);
            webClient.DownloadStringAsync(new Uri("SomeURL"));
        }

        public void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            var rootObject = JsonConvert.DeserializeObject<RootObject1[]>(e.Result);

            foreach (var r in rootObject)
            {
                var s = r.detail.single_selection;
                for (int i = 0; i < s.Count; i++)
                {


                }
            }






        }


        public class RootObject1
        {
            public Detail detail { get; set; }
            [JsonProperty("total questions and marks")]
            public List<TotalQuestionsAndMark> totalquestionsandmarks { get; set; }
        }

        public class TotalQuestionsAndMark
        {
            [JsonProperty("total questions")]
            public int totalquestions { get; set; }
            [JsonProperty("total test marks")]
            public int totaltestmarks { get; set; }
            public string Duration { get; set; }
        }

        public class Detail
        {
            public List<SingleSelection> single_selection { get; set; }            
        }

        public class SingleSelection
        {
            public string Questions { get; set; }
            [JsonProperty("Question Type")]
            public string QuestionType { get; set; }
            public string Url { get; set; }
            public string marks { get; set; }
            public string description { get; set; }
            public Options option { get; set; }
            [JsonProperty("correct Ans")]
            public List<string> correctAns { get; set; }
        }

        public class Options
        {


            public string optionid { get; set; }
            public string option_name { get; set; }


        }
    }
}

I am able to parse some of the data but i am not getting how to parse options.. Please help me in parsing the complete code.Please help me in parsing the complete data... Thanx in advance....

Waleed
  • 3,105
  • 2
  • 24
  • 31
  • Or you colud write your class: http://stackoverflow.com/questions/17446569/json-net-deserealization – Developer Jul 24 '13 at 14:04
  • Sorry, had to remove my answer as it wasn't correct. I think your issue is that in the "options" argument, the JSON uses the maps keys also as values that you have to use. I think you already found out that this is the problem you have. You can probably use the Json.NET's serializer to found out the bits, but I haven't used it. http://james.newtonking.com/projects/json/help/html/SerializingJSONFragments.htm – Johan Paul Jul 24 '13 at 14:52

3 Answers3

0

Here's the sample converter class for you:

// Warning: untested code
public class DictionaryConverter: JsonConverter
{
    public override bool CanRead { get { return true; } }
    public override bool CanWrite { get { return false; } }

    public override bool CanConvert( Type objectType )
    {
        return objectType == typeof( Dictionary<string, string> );
    }

    public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
    {
        // Load JObject from stream
        JObject jObject = JObject.Load( reader );
        Dictionary<string, string> res = new Dictionary<string, string>( jObject.Count );
        foreach( var kvp in jObject )
            res[ kvp.Key ] = (string)kvp.Value;
        return res;
    }

    public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer )
    {
        throw new NotSupportedException();
    }
}

Then declare the options field/property in the following way:

[JsonProperty, JsonConverter( typeof( DictionaryConverter ) )]
public Dictionary<string, string> options;

P.S. You might want to change the converter to create e.g. Dictionary<int, string>, or whatever, instead of just Dictionary<string, string>.

Soonts
  • 20,079
  • 9
  • 57
  • 130
  • You don't really need all of that code, Newtonsoft's json parsing engine already supports IDictionary interface Unlike Microsoft's DataContractJsonSerializer – Waleed Jul 26 '13 at 17:20
0

The issue with your code is that you're trying to parse a dictionary of key value pairs into an array of object items.

This is not possible because the parsing engine will think that optionid and option_name are both attributes on each object item in the options list. If we suppose that these are attributes, the attributes must have a constant name which is not the case in your json.

The only possible way to parse this is using a dictionary of key value pairs of string/string. Your code must be like this, you should drop the Options class.

    [JsonProperty(PropertyName = "options")]
    public Dictionary<string, string> Options { get; set; } 
Waleed
  • 3,105
  • 2
  • 24
  • 31
0

I had a similar issues few months ago, basically the JSON uses properties that cannot be directly translate to C# properties. Thus after some research I found a way to deserialize these properties into one of two possible options (a collection of KeyValuePais or a Dictioanry it will depend of the JSON which option will fits better).

BTW, I am using Json.NET library which let me customize the deserialization process.

These are the files that I am using: For retrieving the content

JsonRetriever.cs using Newtonsoft.Json; using System; using System.Threading.Tasks;

static class JsonRetriever
{

    public async static Task<T> GetJsonFromUri<T>(string uriString)
    {
        var uri = new Uri(uriString);
        return await GetJsonFromUri<T>(uri);
    }

    public async static Task<T> GetJsonFromUri<T>(Uri uri)
    {

        var webRequest = System.Net.WebRequest.CreateHttp(uri);
        using (var webResponse = await Task<System.Net.WebResponse>.Factory.FromAsync(webRequest.BeginGetResponse, webRequest.EndGetResponse, TaskCreationOptions.None))
        {
            using (var stream = webResponse.GetResponseStream())
            {
                return GetJson<T>(stream);
            }
        }
    }

    public static T GetJson<T>(System.IO.Stream stream)
    {
        using (var streamReader = new System.IO.StreamReader(stream))
        {
            using (var jsonTextReader = new JsonTextReader(streamReader))
            {
                var jsonSerializer = JsonSerializer.CreateDefault();
                var instanceOfT = jsonSerializer.Deserialize<T>(jsonTextReader);
                return instanceOfT;
            }
        }
    }

    public static T GetJson<T>(System.String json)
    {
        using (System.IO.TextReader textReader = new System.IO.StringReader(json))
        {
            var jsonSerializer = JsonSerializer.CreateDefault();
            var instanceOfT = (T)jsonSerializer.Deserialize(textReader, typeof(T));
            return instanceOfT;
        }
    }
}

GenericJsonConverter.cs

 /// <summary>
    /// Indicates to Newtonsoft.Json how to deserialize:
    /// - interfaces to concrete classes
    /// - how to deserialize collections of interfaces to concrete classes
    /// - how to deserialize collections of KeyValuePairs (for non-valid identifiers JSON properties) into Dictionary or Collections
    /// See:
    ///      http://stackoverflow.com/questions/5780888/casting-interfaces-for-deserialization-in-json-net
    ///      http://stackoverflow.com/questions/9452901/cannot-deserialize-json-array-into-type-json-net
    /// </summary>
    /// <typeparam name="TInterface"></typeparam>
    /// <typeparam name="TConcrete"></typeparam>
public class GenericJsonConverter<TInterface, TConcrete> : JsonConverter where TConcrete : TInterface
{
    public override bool CanConvert(Type objectType)
    {
        return
             objectType == typeof(Collection<TInterface>)
          || objectType == typeof(TInterface)
          || objectType == typeof(Collection<KeyValuePair<string, TInterface>>)
          || objectType == typeof(Dictionary<string, TInterface>)
          ;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (objectType == typeof(Collection<TInterface>))
        {
            return new Collection<TInterface>(serializer.Deserialize<Collection<TConcrete>>(reader).OfType<TInterface>().ToList());
        }
        if (objectType == typeof(TInterface))
        {
            return serializer.Deserialize<TConcrete>(reader);
        }
        if ((objectType == typeof(Collection<KeyValuePair<string, TInterface>>))
        || (objectType == typeof(Dictionary<string, TInterface>)))
        {

            var isDictionary = (objectType == typeof(Dictionary<string, TInterface>));
            Collection<KeyValuePair<string, TInterface>> deserializedObject = new Collection<KeyValuePair<string, TInterface>>();

            reader.Read(); // Skips the '{' token at the beginning of the JSON object
            while (reader.TokenType == JsonToken.PropertyName)
            {
                var id = reader.Value.ToString();  // Contains the property value => for invalid JSONs properties it's an ID (e.g. number or GUID)
                reader.Read(); // Skips the '{' token of the inner object
                var instaceOfConcreteType = serializer.Deserialize<TConcrete>(reader);
                deserializedObject.Add(new KeyValuePair<string, TInterface>(id, instaceOfConcreteType));
                reader.Read(); // Skips the '{' token at the beginning of the JSON object
            }
            var result = isDictionary ? deserializedObject.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) : (object)deserializedObject;
            return result;
        }

        throw new NotSupportedException("Cannot Deserialize type:" + objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value == null)
            return;
        // TODO: Verify if value.GetType() is compatible with TConcrete before forcing a cast
        if (value.GetType() == typeof(TInterface))
            serializer.Serialize(writer, (TConcrete)value);
        throw new NotSupportedException("Cannot serialize type:" + value.GetType());
    }
}

Models.cs

public class SingleSelection
{
    public string Questions { get; set; }
    public string Url { get; set; }
    public string marks { get; set; }
    public string description { get; set; }


    [JsonConverter(typeof(GenericJsonConverter<string, string>))]
    public Dictionary<string, string> options { get; set; }

    [JsonProperty("Question Type")]
    public string QuestionType { get; set; }

    [JsonProperty("correct Ans")]
    public List<string> correctAns { get; set; }

}

public class Detail
{
    public List<SingleSelection> single_selection { get; set; }
}

public class RootObject
{
    public Detail detail { get; set; }
}

Then the only remaining thing is to use the deserializer:

var rootObject = JsonRetriever.GetJson<List<RootObject>>(AppResources.SampleJson);

Basically, we're customizing the serialization process by indicating how to deal with these edge cases. BTW, the GenericJsonConverteralso works if you want to back your concrete classes with interfaces.

Here is the code sample for Windows Phone 8 https://dl.dropboxusercontent.com/u/7193016/stackoverflow/JsonSamplePhoneApp.zip

Regards, Herber

hmadrigal
  • 1,020
  • 11
  • 19
  • I think during my last test I realized that if you only need a Dictionary then you don't need the GenericJsonConverter . On the other hand, if you want to use Dictionary or Dictionary then GenericJsonConverter is totally useful ;-) – hmadrigal Aug 22 '13 at 22:02