3

I have the following json string as a sample for my problem:

{
    "Code": "Admin",
    "Groups":
    [
        "Administrator",
        "Superuser",
        "User"
    ]
}

Also I have a class named User with code like this...

[JsonObject(MemberSerialization.OptIn)]
public class User
{
    public User (string code)
    {
        this.Code = code;
    }

    [JsonProperty]
    public string Code { get; set; }

    [JsonProperty("Groups")]
    private List<UserGroup> groups;
    public List<UserGroup> Groups
    {
       if (groups == null)
           groups = new List<UserGroup>();
       return groups;
    }
}

... and a class named UserGroup with - for this example - only this few code lines:

public class UserGroup
{
    public UserGroup (string code)
    {
        this.Code = code;

        // Some code to fill all the other properties, just by knowing the code.
    }

    public string Code { get; set; }

    // More properties
}

Now, what I want is that the above shown JSON string would be deserialized into an instance of an User and all strings in the "Groups" array should be deserialized into a List<UserGroup> with instances from each of those strings. Also - the other way round - should a User be serialized into an JSON string with only the Code property of the contained UserGroups.

I don't deserialize normally but I create an instance of an User and populate it with this code...

Newtonsoft.Json.JsonConvert.PopulateObject(jsonString, myUserInstance);

... but if I run the code with the above shown JSON string all I get is the following exception:

Newtonsoft.Json.JsonSerializationException: 'Error converting value "Administrator" to type 'UserGroup'.

My final requirement is that, I want the UserGroup to be serialized as an array of strings only if I serialize the User. When I serialize a UserGroup standalone as a root object, it should be serialized normally (with all properties). (For comparison, in Json.Net: Serialize/Deserialize property as a value, not as an object the object is serialized as a string in all situations.)

dbc
  • 104,963
  • 20
  • 228
  • 340
Christoph Mett
  • 369
  • 3
  • 16
  • 2
    Two options. Create a custom JsonConverter that knows how to deal with the conversion or add implicit operator to the class to know how to convert from a string – Nkosi Aug 02 '17 at 15:31
  • Do I have to create a JsonConverter from String to UserGroup and the other way round oder from string to List? And what do you mean by adding an implicit operator? – Christoph Mett Aug 02 '17 at 15:57
  • Just curious, Why not just use this? [JsonProperty("Groups")] public string[] Groups { get; set; } – Chandra Sekhar V Aug 02 '17 at 15:58
  • Because the UserGroup not only contains the Code property. It was just for easier understanding. – Christoph Mett Aug 02 '17 at 16:02
  • So, your input json string is more like this..{"Code":"Admin","Groups":[{"Code":"Administrator",...},{"Code":"SuperUser",...}]} and not as an array like in the post. – Chandra Sekhar V Aug 02 '17 at 16:15
  • Looks like a duplicate of [Json.Net: Serialize/Deserialize property as a value, not as an object](https://stackoverflow.com/q/40480489/3744182). Does that answer your question or do you need something more specific? – dbc Aug 02 '17 at 22:23
  • Not exactly, @dbc. Mostly it answers my question, but my question was, to only save it as an array of strings, when the user is serialized. With the question you linked, the usergroup would be serialized as only a string even if I serialize itself. But like I've written in the comment below Nkosi's answer I want the usergroup to be serialized as an array of strings only if I serialize the user. If I serialize just the UserGroup itself, it should be serialized normally (with all properties). So if you could remove the "marked as duplicate" (I don't know if you can do this) this would be nice. – Christoph Mett Aug 05 '17 at 13:26

2 Answers2

2

You are going to have to write a converter for the UserGroup

Here is a simple converter based on what was described in the question

public class UserGroupJsonConverter : JsonConverter {

    public override bool CanConvert(Type objectType) {
        return typeof(UserGroup) == objectType;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
        return new UserGroup((string)reader.Value);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
        writer.WriteValue(((UserGroup)value).Code);
    }
}

And then update the User to be aware of the converter by setting the ItemConverterType of the JsonProperty attribute

[JsonObject(MemberSerialization.OptIn)]
public class User {
    public User(string code) {
        this.Code = code;
    }

    [JsonProperty]
    public string Code { get; set; }

    private List<UserGroup> groups;
    [JsonProperty("Groups", ItemConverterType = typeof(UserGroupJsonConverter))]
    public List<UserGroup> Groups {
        get {
            if (groups == null)
                groups = new List<UserGroup>();
            return groups;
        }
    }
}

This would now allow for the JSON in the example

{
    "Code": "Admin",
    "Groups":
    [
        "Administrator",
        "Superuser",
        "User"
    ]
}

to be deserialized as desired

var user = JsonConvert.DeserializeObject<User>(json);

and to be serialized back into the same format.

var json = JsonConvert.SerializeObject(user);
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • And if I do it like this and for example don't want to serialize my user, but maybe the usergroup itself, it doesn't uses this converter (if I don't tell it to), but will serialize it normally, with property names etc., right? – Christoph Mett Aug 05 '17 at 13:19
  • @ChristophMett, Yes. In the above example the attribute was applied only to the `Groups` property on the `User` object. if you want to just serialize the `UserGroup` object on its own the converter does not apply. – Nkosi Aug 05 '17 at 13:24
0

Administrator is a string, UserGroup is an object so this is a type mismatch. The valid JSON for the scenario laid out in your code is:

{
    "Code": "Admin",
    "Groups":
    [
        {
            "Code":"Administrator"
        },
        {
            "Code":"Superuser"
        },
        {
            "Code":"User"
        }
    ]
}
jacobvoller.com
  • 476
  • 7
  • 28
  • Generally this is totally correct but in my sample, I don't want to save the whole object structure of UserGroup in the JSON string but only the code. The code property is enough for my code to get the whole UserGroup instance. So, to not save redundant data (what, I know, is one almost essential part of JSON) I only want to save the string from the code property in the JSON, and also want the deserialization to get the whole UserGroup instance just from the code string. – Christoph Mett Aug 02 '17 at 22:00