0

I have the following JSON (I can't change the incoming JSON as it is from a 3rd party system):

{
  "id": 23,
  "userName":"test@test.com",
  "tags":
  {
     "Employee ID":
     {
        "name":"Employee ID",
        "value":"123456789"
     },
     "Job Family":
     {
       "name": "Job Family",
       "value": "Accounting and Finance"
     }
  }
}

First, I tried to deserialize using this class structure:

public class User
{

    [JsonProperty("id")]
    public int ID { get; set; }
    [JsonProperty("username")]
    public string Email { get; set; }
    [JsonProperty("tags")]
    public TagsJson Tags { get; set; }
}

public class TagsJson
{
    [JsonProperty("tags")]
    public List<Tag> Tags { get; set; }
}

public class Tag
{
    [JsonProperty("name")]
    public string Name { get; set; }
    [JsonProperty("value")]
    public string Value { get; set; }
}

In using Newtonsoft's JsonConvert.DeserializeObject(json);, the User.Tags property is always empty. This is obviously because there are no "Tags" under the "Tags" attribute.

Yet, if I change the User class as follows...

public class User
{

    [JsonProperty("id")]
    public int ID { get; set; }
    [JsonProperty("email")]
    public string Email { get; set; }
    [JsonProperty("tags")]
    public List<Tag> Tags { get; set; }
    // I even tried Tag[] instead of List<Tag> here
}

...I get the following error:

Additional information: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[Tag]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.

Any suggestions on creating the User class to allow it to deserialize correctly would be greatly appreciated.


EDIT

So this may or may not be the best answer, but it works. The tags coming in will be one of a dozen different names. So I created a Tags class with a property for each of the 12 possibilites. Since none are required, any of them that do appear will get populated. Here's my adjusted User, Tags (formerly TagsJson) and Tag classes below - but I'm definitely interested in a better solution.

public class User
{

    [JsonProperty("id")]
    public int ID { get; set; }
    [JsonProperty("username")]
    public string Email { get; set; }
    [JsonProperty("tags")]
    public Tags AllTags { get; set; }
}

public class Tags
{
    [JsonProperty("Employee ID")]
    public Tag EmployeeID { get; set; }
    [JsonProperty("Job Family")]
    public Tag JobFamily { get; set; }        
    // ... and 10 more properties for additional tags that may appear but are not required
}

public class Tag 
{
    [JsonProperty("name")]
    public string Name { get; set; }
    [JsonProperty("value")]
    public string Value { get; set; }
}

The suggested answer of this question does not work in my case because the JSON is not an array or a collection but a list of unique attribute names with a name/value pairing.

Community
  • 1
  • 1
motor75
  • 35
  • 10
  • tags in the example is a single object. if it was an array it would have been wrapped in `[]` – Nkosi May 17 '17 at 21:08
  • Possible duplicate of [How to deserialize json string to object list in c# dot](http://stackoverflow.com/questions/14934360/how-to-deserialize-json-string-to-object-list-in-c-sharp-dot) – Zinov May 18 '17 at 00:09
  • @Zinov both questions are about json deserialization, the answer to the question is pretty trivial and there is already enough info on stackoverflow to solve it. However this question is specifically about deserializing a specific schema, which is not the same as the schema of the data in the question you've linked. – Mick May 18 '17 at 04:34

3 Answers3

0

Not sure your JSON is correct but why is there space in your property name Employee ID or Job Family. Fix those in Tags class and we are good to go.

public class EmployeeID
{
    public string name { get; set; }
    public string value { get; set; }
}

public class JobFamily
{
    public string name { get; set; }
    public string value { get; set; }
}

public class Tags
{
    public EmployeeID __invalid_name__Employee ID { get; set; }
    public JobFamily __invalid_name__Job Family { get; set; }
}

public class User
{
    public int id { get; set; }
    public string userName { get; set; }
    public Tags tags { get; set; }
}

This is what i got from json2csharp.com

Nikhil Agrawal
  • 47,018
  • 22
  • 121
  • 208
  • I did the same - got the same results - the "invalid name" part seems to imply that the JSON is invalid? – motor75 May 17 '17 at 21:09
  • @motor75: You need to confirm this JSON from its source. – Nikhil Agrawal May 17 '17 at 21:10
  • Changing the property names is not required. The "invalid name" thing is because you can't have property names with spaces in C#, but you can in Javascript. The name is not invalid - it's just that C# is not as expressive. Try using `[JsonProperty("Employee ID")] public string EmployeeId { get; set; }` or similar – Alex McMillan May 17 '17 at 21:19
0

Your JSON shows that your tags object is an Object - not an Array. You cannot deserialize an Object into a C# List because they are different structures.

If the keys in tags are dynamic/changing, then perhaps try

[JsonProperty("tags")]
Dictionary<string, string> Tags { get; set; }

Edit

It appears that your JSON is not well-formed; if you aren't able to modify it, you might have to use a custom JsonConverter.

Alex McMillan
  • 17,096
  • 12
  • 55
  • 88
  • I'm thinking that is the solution - except I'm getting this error: Additional information: Unexpected character encountered while parsing value: {. Path 'tags['Employee ID']', line 1, position 835 It appears "Employee ID" needs to have the space removed? – motor75 May 17 '17 at 21:24
  • Well, I removed the space (added an underscore) and that didn't see to fix it: Unexpected character encountered while parsing value: {. Path 'tags.Employee_ID', line 1, position 835. – motor75 May 17 '17 at 21:32
0

Well using the Visual Studio paste Json as classes yields...

public class Rootobject
{
    public int id { get; set; }
    public string userName { get; set; }
    public Tags tags { get; set; }
}

public class Tags
{
    [JsonProperty(PropertyName = "Employee ID")]
    public EmployeeID EmployeeID { get; set; }

    [JsonProperty(PropertyName = "Job Family")]
    public JobFamily JobFamily { get; set; }
}

public class EmployeeID
{
    public string name { get; set; }
    public string value { get; set; }
}

public class JobFamily
{
    public string name { get; set; }
    public string value { get; set; }
}

I had to add in the JsonProperty attributes myself. However I think a better solution would be...

public class Rootobject2
{
    public int id { get; set; }
    public string userName { get; set; }
    public IDictionary<string, NameValuePair> tags { get; set; }
}
public class NameValuePair
{
    public string name { get; set; }
    public string value { get; set; }
}

Although the paste Json as classes was a good starting point to easily copy and paste the code into the favoured solution.

Some test code...

string json = Resource1.String1;

Rootobject test = JsonConvert.DeserializeObject<Rootobject>(json);

Rootobject2 test2 = JsonConvert.DeserializeObject<Rootobject2>(json);
Community
  • 1
  • 1
Mick
  • 6,527
  • 4
  • 52
  • 67
  • This answer worked quite well - and the link showing how to paste JSON into a class was very useful. I put this into my code and all is working. I marked this as the solution and gave it a +1 (it doesn't show up because I'm still too new on this site). Thanks for your help! – motor75 May 18 '17 at 12:02