views:

761

answers:

3

Im trying to use JSON.NET framework in a windows Form to read some information from a JSON string. But im struggling to get the Dictionaries from the 'taxonomies->topics' array and the 'clusters'

{
    "keywords": {
        "anyString": [

        ],
        "allString": {
            "a5349f533e3aa3ccbc27de2638da38d6": "olympics"
        },
        "exactString": [

        ],
        "notString": [

        ],
        "highlightString": [

        ]
    },
    "dates": {
        "startDate": "15-01-2008",
        "endDate": "15-09-2009",
        "useDates": true
    },
    "clusters": {
        "permission": {
            "1": "private\/n"
        }
    },
    "taxonomies": {
        "Topics": {
            "2488": "Olympics 2012 (not participation)",
            "8876": "Olympics and culture"
        },
        "Keywords": {
            "8848": "Engineering in the Olympics"
        }
    },
    "sort": {
        "sortId": 1,
        "sortType": 2,
        "sort": "datetime",
        "sortOrder": "descending"
    }
}

With the code bellow i have been able to read the some of the information.

JObject searchCriteria = JObject.Parse(contentSearchCriteria);  
//search criteria   
IEnumerable<string> allString = searchCriteria["keywords"]["allString"].Children().Values<string>();
IEnumerable<string> anyString = searchCriteria["keywords"]["anyString"].Children().Values<string>();
IEnumerable<string> notString = searchCriteria["keywords"]["notString"].Children().Values<string>();
IEnumerable<string> exactString = searchCriteria["keywords"]["exactString"].Children().Values<string>();
IEnumerable<string> highlightString = searchCriteria["keywords"]["highlightString"].Children().Values<string>();
//dates
string startDate = (string)searchCriteria["dates"]["startDate"];
string endDate = (string)searchCriteria["dates"]["endDate"];
bool useDates = (bool)searchCriteria["dates"]["useDates"];

//sort
int sortId = (int)searchCriteria["sort"]["sortId"];
int sortType = (int)searchCriteria["sort"]["sortType"];
string sort = (string)searchCriteria["sort"]["sort"];
string sortOrder = (string)searchCriteria["sort"]["sortOrder"];

UPDATE:

As recommended I added

class SMSearchCriteria
    {
        public SMKeywords keywords { get; set; }
        public SMDates dates { get; set; }
        public SMClusters clusters { get; set; }
        public SMTaxonomies taxonomies { get; set; }
        public SMSort sort { get; set; }
    }

    class SMKeywords
    {
        public List<Dictionary<string, string>> AnyString {get; set;}
        public List<Dictionary<string, string>> AllString { get; set; }
        public List<Dictionary<string, string>> ExactString { get; set; }
        public List<Dictionary<string, string>> NotString { get; set; }
        public List<Dictionary<string, string>> HighlightString { get; set; } 
    }

    class SMDates
    {
        public string startDate { get; set; }
        public string endDate { get; set; }
        public bool useDates { get; set; }
    }

    class SMClusters
    {
        List<SMCluster> cluster;
    }

    class SMCluster
    {
        public Dictionary<string, string> cluster { get; set; }
    }

     class SMTaxonomies
    {
         public List<SMTaxonomy> taxonomies { get; set; }
    }

     class SMTaxonomy
     {
         public Dictionary<string, List<SMCategory>> taxonomy { get; set; }
     }

     class SMCategory
     {
         public Dictionary<int, string> category { get; set; }
     }

     class SMSort
    {
        public int sortId { get; set; }
        public int sortType { get; set; }
        public string sort { get; set; }
        public string sortOrder { get; set; }
    }

but when i execute:

    var mydata = JsonConvert.DeserializeObject<SMSearchCriteria>(contentSearchCriteria);

I get the Exception:

[Newtonsoft.Json.JsonSerializationException] = {"Cannot deserialize JSON object into type 'System.Collections.Generic.List`1[System.Collections.Generic.Dictionary`2[System.String,System.String]]'."}

Update 2:

As suggested i have removed all the extra lists and simplified the Classes to this:

class SearchMasterSearchCriteria
    {
        public SMKeywords keywords { get; set; }
        public SMDates dates { get; set; }
        public Dictionary<string, Dictionary<int, string>> clusters { get; set; } 
        public Dictionary<string, Dictionary<int, string>> taxonomies { get; set; } 
        public SMSort sort { get; set; }
    }

    class SMKeywords
    {
        public Dictionary<string, string> anyString {get; set;}
        public Dictionary<string, string> allString { get; set; }
        public Dictionary<string, string> exactString { get; set; }
        public Dictionary<string, string> notString { get; set; }
        public Dictionary<string, string> highlightString { get; set; } 
    }

    class SMDates
    {
        public string startDate { get; set; }
        public string endDate { get; set; }
        public bool useDates { get; set; }
    }

     class SMSort
    {
        public int sortId { get; set; }
        public int sortType { get; set; }
        public string sort { get; set; }
        public string sortOrder { get; set; }
    }

I also added test code to serialize the object like this:

//criteria
            SearchMasterSearchCriteria smCriteria = new SearchMasterSearchCriteria();

            //keywords
            SMKeywords smKeywords = new SMKeywords(); ;
            Dictionary<string, string> dict = new Dictionary<string, string>();
            dict.Add("a5349f533e3aa3ccbc27de2638da38d6", "olympics");
            dict.Add("9cfa7aefcc61936b70aaec6729329eda", "games");
            smKeywords.allString = dict;

            //category
            Dictionary<int, string> categorieDict = new Dictionary<int, string>();
            categorieDict.Add(2488, "Olympics 2012 (not participation)");
            categorieDict.Add(8876, "Olympics and culture");

            //taxonomies
            Dictionary<string, Dictionary<int, string>> taxonomiesDict = new Dictionary<string, Dictionary<int, string>>();
            taxonomiesDict.Add("Topics", categorieDict);

            //metadata
            Dictionary<int, string> metadataDict = new Dictionary<int, string>();
            metadataDict.Add(1, @"private/n");

            //clusters
            Dictionary<string, Dictionary<int, string>> clustersDict = new Dictionary<string, Dictionary<int, string>>();
            clustersDict.Add("permission", metadataDict);


            //dates
            SMDates smDates = new SMDates();
            smDates.startDate = "15-01-2008";
            smDates.endDate = "15-09-2009";
            smDates.useDates = true;

            //sort
            SMSort smSort = new SMSort();
            smSort.sortId = 1;
            smSort.sortType = 2;
            smSort.sort = "datetime";
            smSort.sortOrder = "descending";

           //add to criteria.
            smCriteria.keywords = smKeywords;
            smCriteria.clusters = clustersDict;
            smCriteria.taxonomies = taxonomiesDict;
            smCriteria.dates = smDates;
            smCriteria.sort = smSort;

            //serialize
            string json = JsonConvert.SerializeObject(smCriteria);
            var mydata1 = JsonConvert.DeserializeObject<SearchMasterSearchCriteria>(json);

By that time the only difference between the 2 json strings where. the [] and nulls for the anyString, exactString, etc. So i replaced the empty square brackets for curly ones and it desearialized with no errors :)

contentSearchCriteria = contentSearchCriteria.Replace("[]", "{}");
var mydata = JsonConvert.DeserializeObject<SearchMasterSearchCriteria>(contentSearchCriteria);
+4  A: 

To be honest with you I wouldn't do they way you are doing it at all. Here's the way I'd go about retrieving the data:

class Data {
     Dictionary<string, Dictionary<string, string>> keywords;
     DatesClass dates;
     .......

}

class DatesClass
{
    string startDate;
    string endDate;
    bool? useDates

}


var mydata = JsonConvert.DeserializeObject<Data>(jsonstring);

I didn't fill out the entire data class, but you get the point. I've found it much much easier to create a object in the structure of your input data and then use the DeserializeObject method to fill in the data. This also makes the code much cleaner, and allows the compiler to check for typos.

Timothy Baldridge
Hi Timothy, I have updated the question with the classes as you suggested. but I am getting an exception.
Benjamin Ortuzar
+1  A: 

I would start with a strongly-typed dto class first preferably a DataContract that way you get the choice to serialize it to any format you want, i.e. JSON, Xml, ProtoBuf, etc.

Note: JSON is actually pretty slow to serialize/de-serialize compared to most of the other formats (see: serialization benchmarks - JSON.NET is 'NewtonSoft.Json') If you are doing a rich client app instead of web app than you may want to choose a different serialization format. Regardless of which format you end up with, you can still re-use the same DTO, e.g. your code above would looks something like:

[DataContract]
public class MyDto
{
  [DataMember]
  public Keywords keywords { get; set; }
}

[DataContract]
public class Keywords
{
  [DataMember]
  public List<string> anyString { get; set; }

  [DataMember]
  public Dictionary<string,string> allString { get; set; }

  [DataMember]
  public List<string> exactString { get; set; }

  [DataMember]
  public List<string> notString { get; set; }

  [DataMember]
  public List<string> highlightString { get; set; }
}

var dto = new MyDto { Keywords = { allString = {{"a5349f533e3aa3ccbc27de2638da38d6", "olympics"}} };

var json = JsonConvert.SerializeObject(dto);
var fromJson = JsonConvert.DeserializeObject<MyDto>(json);

Edit: added links

It looks like you may be having problems with JSON.NET in which case you should try other JSON .NET serializers/de-serializers. Microsoft ships a System.Runtime.Serialization.Json.DataContractJsonSerializer included in .NET v3.5. Here are some helper classes showing you how to serialize and de-serialize JSON.

Jayrock is another JSON serializer for .NET but it's slower than the rest and I find it doesn't have good support for Generics.

mythz
I receive that JSON from a system i don't have control. I have updated the question with the new classes but I am getting an exception.
Benjamin Ortuzar
ok, then you may be able to make a loosely typed DTO model with just properties of Dictionary<string,string> and List<string>'s?
mythz
Could you give me an example? how would json.net know to which properties to map to?
Benjamin Ortuzar
Effectively I would've done the same as your update. To help debug try the reverse, i.e. populate the object and serialize it to json and then compare the results to your input to see if they match up, alternatively try a different JSON parser link MS's JsonDataContractSerializer, i'll edit with links.
mythz
+1  A: 

Yeah, your issue now is in the way JSON.net deserializes objects. In JSON.net a C# class becomes a Json object. And a member of that class becomes a Key with the value of the member becoming the value.

Let's take the example of the Taxonomy path as an example. Using your above class definition JSON.net is looking for Json data in this format:

{"taxonomies": {"taxonomies":[{"taxonomy": {"Topics": {1212, "foo"}}}]}

This looks nothing at all like your input data.

When you're creating your objects think of it this way.

1) A base object creates a {} in the json code. 2) A dictionary creates a {} in the json code. 3) A List creates a [] in the json code 4) every member of a class creates a entry in the {} of the json code

What might help to debug this for you is to create your structure, fill in some temp data then use JsonConvert.Serialize(myobj) to show you what JSON thinks the structure will look like.

I think your exception comes from having way to many classes.

This is probably what you want the taxominies portion of your code to look like:

class SMSearchCriteria
{
        public SMKeywords keywords { get; set; }
        public SMDates dates { get; set; }
        public SMClusters clusters { get; set; }
        public SMTaxominies taxonomies { get; set; }
        public SMSort sort { get; set; }
}

class SMTaxominies
{
    public Dictionary<string, string> Topics;
    public Keywords<string, string> Keywords;
}
Timothy Baldridge