C# Newtonsoft.Json - Add property during serialization

Asked

Viewed 73 times

-1

Hello!

I’m trying to customize the serialization of a class using the Newtonsoft.Json. My intention is to add a property in JSON during the serialization of the object.

Following the official documentation, managed to implement a JsonConverter custom to add the property. However, when I have nested objects, I cannot apply the JsonConverter in the child objects.

I created a simplified example to demonstrate what happens:

Goal

Add property "Temfilhos" (true or false) on JSON during silkscreen screening.

Target

The class Person will be the target of serialization. If you have any children in the collection, the JSON "Temfilhos" property should be true, but false.

public class Pessoa
{
    public Pessoa() { }

    public Pessoa(string nome, params Pessoa[] filhos)
    {
        Nome = nome;
        if (filhos?.Length > 0)
            Filhos = new List<Pessoa>(filhos);
    }

    public string Nome { get; set; }
    public List<Pessoa> Filhos { get; set; }
}

Jsonconverter

That’s the JsonConverter custom that will add the "Temfilhos" property during serialization.

public class PessoaJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Pessoa);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var pessoa = (Pessoa)value;

        JObject o = JObject.FromObject(pessoa);

        // Adiciona a propriedade "TemFilhos" (true / false)
        o.AddFirst(new JProperty("TemFilhos", new JValue(pessoa?.Filhos?.Count > 0)));
        o.WriteTo(writer, this);
    }
}

Execution

The code below serializes the class Person using the JsonConverter customized.

public class Program
{
    public static void Main(string[] args)
    {
        var ancestral =
            new Pessoa("Goku",
                new Pessoa("Gohan", new Pessoa("Pan")),
                new Pessoa("Goten")
            );

        string json = JsonConvert.SerializeObject(ancestral, new JsonSerializerSettings
        {
            Converters = new List<JsonConverter> { new PessoaJsonConverter() }
        });

        Console.WriteLine(json);
        Console.Read();
    }
}

Expected Result

The goal is to create the "Temfilho" property for all instances of the class Person.

{
    "TemFilhos": true,
    "Nome": "Goku",
    "Filhos": [
        {
            "TemFilhos": true,
            "Nome": "Gohan",
            "Filhos": [
                {
                    "TemFilhos": false,
                    "Nome": "Pan",
                    "Filhos": null
                }
            ]
        },
        {
            "TemFilhos": false,
            "Nome": "Goten",
            "Filhos": null
        }
    ]
}

Result Obtained

The "Temfilhos" property was created only in the first instance of the hierarchy (Goku).

{
    "TemFilhos": true,
    "Nome": "Goku",
    "Filhos": [
        {
            "Nome": "Gohan",
            "Filhos": [
                {
                    "Nome": "Pan",
                    "Filhos": null
                }
            ]
        },
        {
            "Nome": "Goten",
            "Filhos": null
        }
    ]
}

If anyone can help me, I’ll be very hurt!

Issue 1

I guess I didn’t express myself well in the initial post. I know there are other (much simpler) ways to add this property to JSON, as well as solutions alternatives presented in the comments and responses of this post so far.

However, the code I’m working on is much more complex than this example I built. I am developing a type of plugin for my program, and I have no control over the types that will be serialized/deserialized. The first step would be to have a map of Chave => Tipo, and make the Chave be generated in a JSON property, then use it in Deserialization.

I’ve run tests using the option TypeNameHandling = TypeNameHandling.Objects of JsonSerializerSettings, where it generates a property called $type in JSON, with the full name of the class type. That’s more or less what I need, but I want to have control over the name and value of the property that will be generated.

In short, my question is not how to add the "Temfilhos" property to JSON, but how to master the class implementation JsonConverter to the point where I can add the property I need with it.

  • 1

    Unless the intention is otherwise, it would not be more practical less susceptible to errors declare a getter TemFilhos in your class? Something like public bool TemFilhos { get { return this.Filhos.Count > 0; } }

  • Thanks for the help @user140828! But that’s not quite what I was looking for. I edited the post explaining the situation better.

2 answers

0


Solution

I was able to find a solution. My problem was in converting the class instance into a JObject before proceeding with serialization. When I did this, the type of class was lost, and so the daughter instances did not go through the JsonConverter.

The solution was to turn the data into a dictionary, and serialize the dictionary.

public class PessoaJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Pessoa);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var pessoa = (Pessoa)value;

        var dic = new Dictionary<string, object>()
        {
            {"TemFilhos",  pessoa?.Filhos?.Count > 0 },
            {nameof(pessoa.Nome ), pessoa.Nome },
            {nameof(pessoa.Filhos ), pessoa.Filhos}
        };
        serializer.Serialize(writer, dic);
    }
}

0

Hello!

I would do it this way. First add an Id and a Parenteid to the Person.Cs class

    public int Id { get; set; }
    public string Nome { get; set; }
    public int? ParenteId { get; set; }
    public IEnumerable<Pessoa> Filhos { get; set; }

Then create a recursive function to add the children, searching for the Id == Parenteid, if you have children add to the personAtual.

    static IEnumerable<Pessoa> ConstruirRelacao(Pessoa pessoaAtual, Pessoa[] pessoas)
    {
        var filhos = pessoas.Where(c => c.ParenteId == pessoaAtual.Id).ToArray();

        foreach (var filho in filhos)
            filho.Filhos = ConstruirRelacao(filho, pessoas);

        pessoaAtual.Filhos = filhos;
        return filhos;
    }

Now in the Program class I create a list by passing the objects to her, referencing the parent id to the children (Parenteid). Then I call the function to create the relationship of the Father and children.

        IList<Pessoa> pessoas = new List<Pessoa>()
        {
            new Pessoa(){Id = 1, Nome = "Goku", ParenteId = null, Filhos = new List<Pessoa>() },
            new Pessoa(){Id = 2, Nome = "Gohan", ParenteId = 1, Filhos = new List<Pessoa>() },
            new Pessoa(){Id = 3, Nome = "Pan", ParenteId = 2, Filhos = new List<Pessoa>() },
            new Pessoa(){Id = 4, Nome = "Goten", ParenteId = 1, Filhos = new List<Pessoa>() },
        };

        var Pai = pessoas.Where(i => i.ParenteId == null).FirstOrDefault();

        Program.ConstruirRelacao(Pai, pessoas.ToArray());

        Console.WriteLine(JsonConvert.SerializeObject(Pai));

        Console.ReadKey();

Return:

{
   "Id":1,
   "Nome":"Goku",
   "ParenteId":null,
   "Filhos":[
      {
         "Id":2,
         "Nome":"Gohan",
         "ParenteId":1,
         "Filhos":[
            {
               "Id":3,
               "Nome":"Pan",
               "ParenteId":2,
               "Filhos":[
                  
               ]
            }
         ]
      },
      {
         "Id":4,
         "Nome":"Goten",
         "ParenteId":1,
         "Filhos":[
            
         ]
      }
   ]
}

I don’t know if this is what you really wanted, but I hope I helped you with something.

Att,

  • Thanks for the help Lucas! Not quite what I needed. I edited the post to explain the situation better. I want to get it done using JsonConverter.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.