Home > OS >  How to output a formatted string from a JSON model class
How to output a formatted string from a JSON model class

Time:07-16

I want something like the following:

using System;
using System.Text;
using System.Text.Json;
                    
public class Program
{
   public static void Main()
   {
      var myArgs = new ArgClass();
        
      // myArgs is populated via System.Text.Json deserialization
        
      Console.WriteLine(myArgs.ToArguments());
   }
}

public interface IArguments {}

public class ArgClass : IArguments
{
   [JsonPropertyName("propertyA")]
   public string PropA {get; set;}
    
   [JsonPropertyName("propertyA")]
   public int PropB {get; set;}
}

public static class IArgumentsExt
{
   public static string ToArguments(this IArguments ArgObj)
   {
      var argString = new StringBuilder();

      //Code to build argument string

      return argString.ToString();
    } 
}

So that the Console.Writeline() would output the following string:

-argument propertyA="some text" -argument propertyB=123

I feel like this is something that has to be done with reflection, but I know nothing about reflection. I have tried really hard to avoid situations that require it because everything I read (or watch) says it's slow.

The reason I want to do it like this is that the JSON string that I am deserializing has more properties that I am interested in at to moment, but I might want to use more of them in the future; like if I add more features to my app later. As it stands I can add PropC to ArgClass to pick up another JSON property. In which case I would want the new output of ToArguments() to be:

-argument propertyA="text a" -argument propertyB=123 -argument propertyC="text c"

without having to modify ToArguments().

Or maybe make another class:

public class OtherArgClass : IArguments
{
   [JsonPropertyName("propertyD")]
   public string PropD {get; set;}
    
   [JsonPropertyName("propertyE")]
   public int PropE {get; set;}
}

and have an output of myOtherArgs.ToArguments():

-argument propertyD="some other text" -argument propertyE=45.678

again, without modifying ToArguments().

I am using .Net 7 Preview and System.Text.Json (not Newtonsoft/Json.net)

CodePudding user response:

Yes, System.Reflection is needed to achieve your required value.

  1. Get the properties of the instance which applies the JsonPropertyName attribute.

  2. Iterate the properties.

    2.1. Get the property name from the JsonPropertyName attribute.

    2.2. Get the property value and serialize.

    2.3. Add both values into StringBuilder.

using System.Reflection;
using System.Linq;

public static class IArgumentsExt
{
   public static string ToArguments(this IArguments ArgObj)
   {
      var argString = new StringBuilder();
       
       PropertyInfo[] props = ArgObj.GetType().GetProperties()
           .Where(x => x.CustomAttributes.Any(y => y.AttributeType.Name == nameof(JsonPropertyNameAttribute)))
           .ToArray();

       foreach (PropertyInfo prop in props)
       {       
           string propertyName = ((JsonPropertyNameAttribute)prop.GetCustomAttribute(typeof(JsonPropertyNameAttribute))).Name;

           // Or
           // string propertyName = prop.GetCustomAttribute<JsonPropertyNameAttribute>().Name;

           string propertyValue = JsonSerializer.Serialize(prop.GetValue(ArgObj));
           argString.Append($"-argument {propertyName}={propertyValue} ");
       }

      return argString.ToString()
          .Trim();
    } 
}

Sample .NET Fiddle

  • Related