1

I have a big problem with deserializing my JSON to an object. It should be deserialized to IList<KeyValuePair<string, object>> the problem is that the keys have white spaces.

"spec": {
         "SOMETHING WITH SPACES" : "10"
         etc. 
         ...
}
public class SomeObject
{
...
public IList<KeyValuePair<string, object>> spec{ get; set; }
...
}

Deserializing code:

var sr = new ServiceStack.Text.JsonSerializer<SomeObject>();
var esResult = sr.DeserializeFromString(responseJson);

responseJson is a GET from ElasticSearch.

What I get to my field it is null. If I have key without whitespaces it's deserializing normally and I'm getting my IList<KeyValuePair<string, object>>

d219
  • 2,707
  • 5
  • 31
  • 36
Skundlony
  • 61
  • 8
  • 2
    post the entire json – Kunal Mukherjee May 29 '19 at 09:31
  • 2
    Why not just use `Dictionary` instead of `IList>`? – dbc May 29 '19 at 09:32
  • Typically interfaces are problematic when it comes to serialization. There is probably no way for the serializer to know where to get an implementation of the `IList` you have there. If a serializer adds more information w.r.t. the implementation in the serialized representation then that may be used to deserialize but that is something that typically does not happen in the JSON world. Try to change that to only `List` and not `IList`. – Eben Roux May 29 '19 at 09:39
  • I cannot use Dictionary, because that object it's used in a few places like List too. Changing IList to List doesn't work, but thanks for suggestion. – Skundlony May 29 '19 at 09:48
  • Please see my answer for how to parse using a Dictionary and still get the List that you need. – Peter B May 29 '19 at 10:04

3 Answers3

2

You can't use IList or List here, because your source JSON has no [ ] in it, which is a requirement if you want to parse into such a collection. In other words, without [ ] you can't parse into a collection, at least not without going through lots of hoops.

Instead you need to use a Dictionary as was suggested already in comments.

Note: I used Newtonsoft JsonConvert because you didn't state what your parser is, but that should make little or no difference to my arguments.

Working code:

var json = "{ \"spec\": { \"SOMETHING WITH SPACES\" : \"10\" } }";
var someObj = JsonConvert.DeserializeObject<SomeObject>(json);

public class SomeObject
{
    public Dictionary<string, object> spec{ get; set; }
}

After that, you can cast the spec property to an IEnumerable and loop through whatever was found:

foreach (var pair in someObj.spec as IEnumerable<KeyValuePair<string, object>>)
{
    Console.WriteLine(pair.Key + " -> " + pair.Value);
}

Or even convert it to a List:

var list = someObj.spec.ToList();
foreach (var pair in list)
{
    Console.WriteLine(pair.Key + " -> " + pair.Value);
}

.NET Fiddle: https://dotnetfiddle.net/15l2R3

Peter B
  • 22,460
  • 5
  • 32
  • 69
  • I must use `List>` because my specification can have same named keys. – Skundlony May 29 '19 at 11:59
  • In that case, see [this page](https://dzone.com/articles/duplicate-keys-in-json-objects) about having JSON with duplicate keys. It states "(a parser could choose to) return all the key-value pairs, or it may even reject the JSON with a parsing error, and all of these behaviours would be valid. That said, most popular implementations ... follow the rule of taking only the last key-value pair". I am not aware of any parsers that deal with it by keeping all the duplicates, and you may have a hard time finding one. – Peter B May 29 '19 at 13:52
0

I guess your JSON serialiazer makes some trouble. I'd recommend to use Newtonsoft.Json (in NuGet) I've tried following code, and it works fine:

        var o1 = new SomeObject() { spec = new List<KeyValuePair<string, object>>() };
        o1.spec.Add(new KeyValuePair<string, object>("test with spaces", 10));

        var r1 = Newtonsoft.Json.JsonConvert.SerializeObject(o1);
        Console.WriteLine(r1);
        var o2 = Newtonsoft.Json.JsonConvert.DeserializeObject<SomeObject>(r1);
        var r2 = Newtonsoft.Json.JsonConvert.SerializeObject(o2);
        Console.WriteLine(r2);

The outcome is

{"spec":[{"Key":"test with spaces","Value":10}]}
{"spec":[{"Key":"test with spaces","Value":10}]}

No null values, all works fine.

EDIT: I actually see no reason, why spaces should be any problem at all. They are just part of the string.

Malior
  • 1,221
  • 8
  • 16
  • Note that the JSON that you generate and then parse has `[ ]` in it, whereas OP has no `[ ]` in their JSON. – Peter B May 29 '19 at 09:45
  • Newstonsoft.Json saves output with Key and Value words. I have a lot of indexes in elasticsearch saved with ServiceStack json and it's looks a bit diffrent. – Skundlony May 29 '19 at 09:46
  • You are right, this is quite different. I would recommend, like Kunal Mukherjee, to post a full json-file example to better get how it's formated. – Malior May 29 '19 at 10:52
0

If you don't mind using Newtonsoft.Json:

    const string json = @"{""spec"": { ""SOMETHING WITH SPACES"" : ""10"", ""SOMETHING WITH MORE SPACES"" : ""20"" }}";
    dynamic data = JsonConvert.DeserializeObject(json);
    Dictionary<string, string> list = data["spec"].ToObject<Dictionary<string, string>>();
    foreach (var item in list)
    {
        Console.WriteLine(item.Key + ", " + item.Value);
    }
Bart van der Drift
  • 1,287
  • 12
  • 30