Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

I'm trying to get my C# class to serialize to a json that complies with GP Job, for example:

 {
      "jobId" : "jb10b153b77e74a839e31af53e5d4d0d1", 
      "jobStatus" : "esriJobSucceeded", 
      "results" : {
        "output_Buffer" : {"paramUrl" : "results/output_Buffer"}, 
        "Final_wellpoints" : {"paramUrl" : "results/Final_wellpoints"}
      }, 
      "inputs" : {
        "Input_Features" : {"paramUrl" : "inputs/Input_Features"}, 
        "Distance" : {"paramUrl" : "inputs/Distance"}
      }, 
      "messages" : [
        {"type" : "esriJobMessageTypeInformative", "description" : "Submitted."}, 
        {"type" : "esriJobMessageTypeInformative", "description" : "Executing..."}, 
        {"type" : "esriJobMessageTypeInformative", "description" : "Succeeded."}
      ]
    }

But my dictionaries (results and inputs) serialize with key/value cruft:

//
{
        "jobId":"jb10b153b77e74a839e31af53e5d4d0d1", 
        "jobStatus":"esriJobSucceeded", 
        "results":[    
            {"Key":"output_Buffer",     "Value":      {"paramUrl":"results/output_Buffer"}},    
            {"Key":"Final_wellpoints",     "Value":        {"paramUrl":"results/Final_wellpoints"}}], 
        "inputs":[    
            {"Key":"Input_Features",     "Value":        {"paramUrl":"inputs/Input_Features"}},    
            {"Key":"Distance",     "Value":        {"paramUrl":"inputs/Distance"}}], 
        "messages":[    
            {"type":"esriJobMessageTypeInformative",     "description":"Submitted."},    
            {"type":"esriJobMessageTypeInformative",     "description":"Executing..."},    
            {"type":"esriJobMessageTypeInformative",     "description":"Succeeded."}
        ]
}

Update: I've commented out the WCF code and replaced with Json.NET call.

[DataContract]
public class GPJob
{
    public static string Test()
    {

        Dictionary<string,taskParam> resDict = new Dictionary<string,taskParam>();
        resDict.Add("output_Buffer",new taskParam(){ paramUrl = "results/output_Buffer"});
        resDict.Add("Final_wellpoints",new taskParam(){ paramUrl = "results/Final_wellpoints"});
        Dictionary<string,taskParam> inputDict = new Dictionary<string,taskParam>();
        inputDict.Add("Input_Features", new taskParam(){paramUrl="inputs/Input_Features"});
        inputDict.Add("Distance", new taskParam(){paramUrl="inputs/Distance"});

        GPJob job = new GPJob
        {
            jobId = "jb10b153b77e74a839e31af53e5d4d0d1",
            jobStatus = "esriJobSucceeded",
            results = resDict,
            inputs = inputDict,
            messages = new List<message>()
            {
                new message(){type="esriJobMessageTypeInformative",description = "Submitted."},
                new message(){type="esriJobMessageTypeInformative",description = "Executing..."},
                new message(){type="esriJobMessageTypeInformative",description = "Succeeded."}
            }

        };
        string json = JsonConvert.SerializeObject(job,Formatting.Indented);
        return json;
        /*
        DataContractJsonSerializer ser = new DataContractJsonSerializer(job.GetType());
        System.IO.MemoryStream ms = new System.IO.MemoryStream();
        ser.WriteObject(ms, job);
        string json = Encoding.Default.GetString(ms.ToArray());
        return json;
            */
    }

    [DataMember(Order=0)]
    public string jobId { get; set; }
    [DataMember(Order = 1)]
    public string jobStatus { get; set; }

    [DataMember(Order = 2)]
    public Dictionary<string, taskParam> results { get; set; }

    [DataMember(Order = 3)]
    public Dictionary<string, taskParam> inputs { get; set; }
    [DataMember(Order = 4)]
    public List<message> messages { get; set; }
}

[DataContract]
public class taskParam
{
    [DataMember]
    public string paramUrl { get; set; }
}
[DataContract]
public class message
{
    [DataMember(Order=0)]
    public string type { get; set; }
    [DataMember(Order = 1)]
    public string description { get; set; }
}

Here's the output:

{
  "jobId": "jb10b153b77e74a839e31af53e5d4d0d1",
  "jobStatus": "esriJobSucceeded",
  "results": {
    "output_Buffer": {
      "paramUrl": "results/output_Buffer"
    },
    "Final_wellpoints": {
      "paramUrl": "results/Final_wellpoints"
    }
  },
  "inputs": {
    "Input_Features": {
      "paramUrl": "inputs/Input_Features"
    },
    "Distance": {
      "paramUrl": "inputs/Distance"
    }
  },
  "messages": [
    {
      "type": "esriJobMessageTypeInformative",
      "description": "Submitted."
    },
    {
      "type": "esriJobMessageTypeInformative",
      "description": "Executing..."
    },
    {
      "type": "esriJobMessageTypeInformative",
      "description": "Succeeded."
    }
  ]
}

Update2 Looks like I spoke too soon, deserialization of this string fails:

string json = "{\"jobId\":\"jeabfba358b69412abeafd63e415957bf\",\"jobStatus\":\"esriJobWaiting\"," + 
                         "\"results\":[],\"inputs\":[],\"messages\":[]}";
GPJob j= JsonConvert.DeserializeObject(json, typeof(GPJob),JsonSerializerSettings) as GPJob;

I get this exception:

Cannot deserialize JSON array into type 'System.Collections.Generic.Dictionary`2[System.String,Anadarko.iMaps.JobLib.taskParam]'

It works ok when the results and inputs are not empty.

Update3:

This string deserializes:

{"jobId":"jeabfba358b69412abeafd63e415957bf","jobStatus":"esriJobWaiting","results":null,"inputs":null,"messages":null}

So here's the hack:

// hack: replace [] with null
string hackedJson = json.Replace("[]", "null");
GPJob j= JsonConvert.DeserializeObject(hackedJson, typeof(GPJob)) as GPJob;

Of course this results in a null instead of an empty dictionary. Any idea where that might cause an issue (if so, I guess I could handle that in the setters).

share|improve this question
    
Huh. In some respects, I'd be tempted to say that Json.NET is right - you can't deserialize an array into a dictionary. Does the bit below work better: string json = "{\"jobId\":\"jeabfba358b69412abeafd63e415957bf\",\"jobStatus\":\"esriJobWaiting‌​\"," + "\"results\":{},\"inputs\":{},\"messages\":[]}"; Here, we're saying that both results and inputs are null objects, not empty arrays. –  Herb Oct 5 '10 at 17:48
    
Thanks, herb, I've done a hack with a variant of your suggestion. –  Kirk Kuykendall Oct 5 '10 at 18:11
    
So you're going to be getting broken json data? Ick. With respect to the null values, have you played with the JsonSerializerSettings to see if those would help you out? It seems as though you could set NullValueHandling to Ignore, and it would leave the dictionaries alone (assuming you'd created them in the class constructor). –  Herb Oct 5 '10 at 18:31
    
Setting to ignore fixes it so my member dictionaries aren't set to null, they remain empty dictionaries, created in the constructor. Thanks again. –  Kirk Kuykendall Oct 5 '10 at 18:46

2 Answers 2

up vote 1 down vote accepted

According to this, I'm going to say probably not, at least not without doing some extra work yourself. You can override the values used for Key and Value, but you're going to get the same thing as you have. You need a way to ditch representation of properties as a collection - as the underlying runtime appears to treat the dictionary as IEnumerable.

I think the short answer is to not use DataContractSerializer, and instead use an alternative method, such as Json.NET.

share|improve this answer
    
Thanks Herb, Json.NET is pretty simple, plus it works. –  Kirk Kuykendall Oct 4 '10 at 22:58
    
I've only been playing with it for ... oh, a day, and yeah, it's pretty nice. Wouldn't have found it were it not for your question. –  Herb Oct 5 '10 at 12:37

Apparently that's how the DataContractJsonSerializer is supposed to work, per this bug submittal at Microsoft Connect.

A workaround for this is referred to in the same post.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.