0

I am using Newtonsoft to serialize and deserialize objects. Here is a sample of how my data is structured:

[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
public class BaseSerializer
{
    [JsonProperty("players")] public IList<PlayersSerializer>? Players { get; set; }
}

[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
public class PlayersSerializer
{
    [JsonProperty("player1")] public PlayerSerializer? Player1{ get; set; }
    
    [JsonProperty("otherProp")] public string? OtherProp { get; set; }
}

[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
public class PlayerSerializer
{
    [JsonProperty("name")] public string? Name{ get; set; }
}

If I pass in this JSON:

{
  "players": [
    {
      "player1": {
        "name": "Robert Bobbert"
      },
      "otherProp": "Tasty"
    }
  ]
}

To:

var test = JsonConvert.DeserializeObject<BaseSerializer>(json);

Then define this custom ContractResolver to rename the property:

public class MyCustomContractResolver: DefaultContractResolver
{
    public static readonly MyCustomContractResolverInstance = new();

    private readonly Dictionary<Type, Dictionary<string, string>> _propertyNameConverter = new()
    {
        {
            typeof(PlayerSerializer), new Dictionary<string, string>
            {
                { "name", "player1Name" },
            }
        },
    };

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);

        if (_propertyNameConverter.ContainsKey(property.DeclaringType) &&
            _propertyNameConverter[property.DeclaringType].ContainsKey(property.PropertyName))
        {
            property.PropertyName = _propertyNameConverter[property.DeclaringType][property.PropertyName];
        }
        return property;
    }
}

I get the following output with "name" renamed to "player1Name" when running this code:

Console.Write(JToken.Parse(JsonConvert.SerializeObject(test, new JsonSerializerSettings { ContractResolver = MyCustomContractResolver.Instance})));

{
  "players": [
    {
      "player1": {
        "player1Name": "Robert Bobbert"
      },
      "otherProp": "Tasty"
    }
  ]
}

Now the problem is that I would like "player1Name" to be directly under the root for the sake of this example (I would like this to be generic in the sense that in some cases I might want it under "players" or somewhere else). Meaning that my desired output would be:

{
  "player1Name": "Robert Bobbert",
  "players": {
    "otherProp": "Tasty"
  }
}

I tried accessing the parent properties and/or changing some properties on "property" in the ContractResolver to no avail.

For another example, I basically want what another user asked for here: Newtonsoft Json How to write properties from inner class to parent class with out creating of inner class object, but the only answer there mentions using dynamic and I'd rather not do it that way.

Jumbala
  • 4,764
  • 9
  • 45
  • 65
  • What happens if the `public IList? Players` contains more than one `PlayersSerializer`? What do you want to see then? – dbc Aug 24 '22 at 18:50
  • A contract resolver resolves the contract for a specific type. What you seem to want to do here is to restructure information between different types. While it isn't impossible to do this with a contract resolver (see e.g. [Json.NET Custom ValueProvider to convert Objects into Guid](https://stackoverflow.com/a/39818891/3744182)) Json.NET isn't going to make it easy. Restructuring data between some subgraph of objects is really best handled by a custom `JsonConverter` and/or injection of a DTO. – dbc Aug 24 '22 at 18:57
  • See also [JSON.net ContractResolver vs. JsonConverter](https://stackoverflow.com/a/41094764/3744182) where the answer by Brian Rogers suggests using a `JsonConverter` to *Translate between differing JSON and object structures*, and gives multiple examples. – dbc Aug 24 '22 at 18:58
  • @dbc To answer your first question, the specifics don't really matter as this is just a self-contained example and for some fields I might want to move the data to a parent while for other I might want to leave them as-is. The goal is mainly to translate one JSON object to another in a different format (that I don't have control over). I wasn't aware of JsonConverter and from what I have read so far by consulting your links, it seems like it would be the correct solution to what I want to accomplish. Thanks for your answers, I really appreciate it! :) – Jumbala Aug 24 '22 at 20:09

1 Answers1

1

you can parse json and convert to a needed structure

    var jsonParsed = JObject.Parse(json);

    Player player = new Player
    {
        Player1Name = (string)jsonParsed["players"][0]["player1"]["name"],
        Players = new Players
        {
            OtherProp = (string)jsonParsed["players"][0]["otherProp"]
        }

    };

[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
public class Player

{
    [JsonProperty("player1Name")]
    public string Player1Name { get; set; }

    [JsonProperty("players")]
    public Players Players { get; set; }
}

[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
public partial class Players
{
    [JsonProperty("otherProp")]
    public string OtherProp { get; set; }
}

or you can use a dictionary if you have several properties, not just OtherProp

    var playerName = (string)jsonParsed["players"][0]["player1"]["name"];

    ((JObject)jsonParsed["players"][0]).Properties()
                    .Where(x => x.Name == "player1").First().Remove();

    Player player = new Player
    {
        Player1Name = playerName,
        Players = jsonParsed["players"][0].ToObject<Dictionary<string, object>>()
    };

[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
public class Player

{
    [JsonProperty("player1Name")]
    public string Player1Name { get; set; }

    [JsonProperty("players")]
    public Dictionary<string, object> Players { get; set; }
}
Serge
  • 40,935
  • 4
  • 18
  • 45