2

I have to read a JSON stream (which I have no control over), which is in the form:

{"files":
    {
        "/some_file_path.ext": {"size":"1000", "data":"xxx", "data2":"yyy"},
        "/other_file_path.ext": {"size":"2000", "data":"xxx", "data2":"yyy"},
        "/another_file_path.ext": {"size":"3000", "data":"xxx", "data2":"yyy"},
    }
}

So, I have an object named files, which has a number of properties, which have 1) different names every time, 2) different number of them every time, and 3) names with characters which can't be used in C# properties.

How do I deserialize this?

I'm putting this into a Portable Library, so I can't use the JavaScriptSerializer, in System.Web.Script.Serialization, and I'm not sure about JSON.NET. I was hoping to use the standard DataContractJsonSerializer.


UPDATE: I've changed the sample data to be closer to the actual data, and corrected the JSON syntax in the area the wasn't important. (Still simplified quite a bit, but the other parts are fairly standard)

James Curran
  • 101,701
  • 37
  • 181
  • 258
  • That's not even valid JSON. Are you sure that's what you need to support? – Amit Nov 17 '15 at 06:30
  • 1
    You can deserialize JSON objects as Dictionaries. It allows different names of keys, different number and names which can't be used in C# properties. However, the provided string is not JSON. – Yeldar Kurmangaliyev Nov 17 '15 at 06:33
  • 2
    You may need to investigate using Json.NET. Your JSON properties are not properly enclosed in quotes, as required by the [standard](http://json.org/): `{"files": { ... }}`. See [here](http://stackoverflow.com/questions/7768290). Json.NET *will* parse JSON with missing quotes for property names, so if you can't fix the JSON you may need to use it. – dbc Nov 17 '15 at 07:05
  • JavaScriptSerializer can be a bit picky about JSON validation. Make sure to wrap the JSON strings in quotes. { "files": { "some_file_path.ext": { "size": 1000, "data": "xxx", "data2": "yyy" }, "other_file_path.ext": { "size": 2000, "data": "xxx", "data2": "yyy" }, "another_file_path.ext": { "size": 3000, "data": "xxx", "data2": "yyy" } } } – sjokkogutten Nov 17 '15 at 07:25
  • @dbc - The best answer seems to be to use Json.NET, which apparently is compatible with a Portable Library. Write it up as an answer and I'll accept it. (Note, the actual data had all the quotes; I just originally left them off the example since that part wasn't where the problem was) – James Curran Nov 17 '15 at 17:39
  • @JamesCurran - if the actual data has quotes, you can still use `DataContractJsonSerializer` if you want. – dbc Nov 17 '15 at 17:41

3 Answers3

3

You can model your "files" object as a Dictionary keyed by the JSON property name:

public class RootObject
{
    public Dictionary<string, PathData> files { get; set; }
}

public class PathData
{
    public int size { get; set; }
    public string data { get; set; }
    public string data2 { get; set; }
}

Then, only if you are using .Net 4.5 or later, you can deserialize using DataContractJsonSerializer, but you must first set DataContractJsonSerializerSettings.UseSimpleDictionaryFormat = true:

        var settings = new DataContractJsonSerializerSettings { UseSimpleDictionaryFormat = true };
        var root = DataContractJsonSerializerHelper.GetObject<RootObject>(jsonString, settings);

With the helper method:

public static class DataContractJsonSerializerHelper
{
    public static T GetObject<T>(string json, DataContractJsonSerializer serializer = null)
    {
        using (var stream = GenerateStreamFromString(json))
        {
            var obj = (serializer ?? new DataContractJsonSerializer(typeof(T))).ReadObject(stream);
            return (T)obj;
        }
    }

    public static T GetObject<T>(string json, DataContractJsonSerializerSettings settings)
    {
        return GetObject<T>(json, new DataContractJsonSerializer(typeof(T), settings));
    }

    private static MemoryStream GenerateStreamFromString(string value)
    {
        return new MemoryStream(Encoding.Unicode.GetBytes(value ?? ""));
    }
}

Alternatively, you can install Json.NET and do:

        var root = JsonConvert.DeserializeObject<RootObject>(jsonString);

Json.NET automatically serializes dictionaries to JSON objects without needing to change settings.

dbc
  • 104,963
  • 20
  • 228
  • 340
1

We need to first convert this Invalid JSON to a Valid JSON. So a Valid JSON should look like this

{
    "files": 
    {
        "FilePath" : "C:\\some\\file\\path",
        "FileData" : {
            "size": 1000,
            "data": "xxx",
            "data2": "yyy"
        },
        "FilePath" :"C:\\other\\file\\path",
        "FileData" : {
            "size": 2000,
            "data": "xxx",
            "data2": "yyy"
        },
        "FilePath" :"C:\\another\\file\\path",
        "FileData" : {
            "size": 3000,
            "data": "xxx",
            "data2": "yyy"
        }
    }
}

To make it a valid JSON we might use some string functions to make it looks like above. Such as

MyJSON = MyJSON.Replace("\\", "\\\\");
MyJSON = MyJSON.Replace("files", "\"files\"");
MyJSON = MyJSON.Replace("data:", "\"data:\"");
MyJSON = MyJSON.Replace("data2", "\"data2\"");
MyJSON = MyJSON.Replace(": {size", ",\"FileData\" : {\"size\"");
MyJSON = MyJSON.Replace("C:", "\"FilePath\" :\"C:");

Than we can create a class like below to read the

public class FileData
{
    public int size { get; set; }
    public string data { get; set; }
    public string data2 { get; set; }
}

public class Files
{
    public string FilePath { get; set; }
    public FileData FileData { get; set; }
}

public class RootObject
{
    public Files files { get; set; }
}
Mohit S
  • 13,723
  • 6
  • 34
  • 69
0

Assuming you have a valid JSON you could use JavaScriptSerializer to return a list of objects

string json = "{}"
var serializer = new JavaScriptSerializer();
var deserializedValues = (Dictionary<string, object>)serializer.Deserialize(json, typeof(object));

Alternatively you could specify Dictionary<string, List<string>> as the type argument

strign json = "{}";
JavaScriptSerializer serializer = new JavaScriptSerializer();
var deserializedValues = serializer.Deserialize<Dictionary<string, List<string>>>(json);

foreach (KeyValuePair<string, List<string>> kvp in deserializedValues)
{
    Console.WriteLine(kvp.Key + ": " + string.Join(",", kvp.Value));
}
sjokkogutten
  • 2,005
  • 2
  • 21
  • 24
  • But System.Web.Script.Serialization.JavaScriptSerializer is not available in a Portable Library. – James Curran Nov 17 '15 at 12:54
  • You have to add a reference to [`System.Web.Extensions`](https://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer.aspx?f=255&MSPPError=-2147217396), but I'm not sure if it is available in a Portable Library. Right click project -> Select 'Properties' -> Select 'References' -> 'Add' -> 'Assemblies' -> 'Framework' -> Scroll down to 'System.Web.Extensions' and check the box next to it. – sjokkogutten Nov 17 '15 at 13:07