0

I'm working on a C# program for JSON serialisation.

My object looks as follows (example of a property):

public class Device
{
    [JsonProperty("ALLTYPES_NAME")]
    public string ALLTYPES_NAME { get; set; }
    [JsonProperty("INFORMATION")]
    public string INFORMATION { get; set; }
    ...

Now I have the following information (in a textfile):

ALLTYPES_NAME  "Object1"
ALLTYPES_NAME  "Object2"
INFORMATION    "Inside_Info"

I would like to create two objects, which are to be JSON serialised as follows:

"desired_objects": [
{
  "ALLTYPES_NAME": "Object1",
},
{
  "ALLTYPES_NAME": "Object2",
  "INFORMATION": "Inside_Info,
  ...

In order to get this done, I need something like:

temp_obj.GetPropertyByName("ALLTYPES_NAME") = "Object1";
desired_objects.Add(temp_obj);
...
temp_obj.GetPropertyByName("ALLTYPES_NAME") = "Object2";
temp_obj.GetPropertyByName("INFORMATION")   = "Inside_Information";
...

One way to do this, is working with templates. However I wonder if this is needed, seen the fact that the needed information is retrievable using the [JsonProperty] directives, hence my question:

Does a method .GetPropertyByName() exist, based on the [JsonProperty ...]? (Or even larger, can the [JsonProperty] directives be used for something else rather than the JSON serialiser?)

dbc
  • 104,963
  • 20
  • 228
  • 340
Dominique
  • 16,450
  • 15
  • 56
  • 112
  • 2
    `example of a field` That is a property, **not** a field. – mjwills Jul 26 '21 at 11:42
  • But basically, you should use `Type.GetProperties()` to get the properties, and for each `PropertyInfo` you can check whether it has the `JsonPropertyAttribute` attribute. As an aside, I'd strongly advise you to follow .NET naming conventions for your properties, so `AllTypesName` and `Information` for example - a large part of the point of `JsonPropertyAttribute` is to allow you to use idiomatic .NET names for the properties in code, regardless of how they're represented in JSON. – Jon Skeet Jul 26 '21 at 11:44
  • Do you know you are doing by hand what serializer does in 1 line of code ? Any particular reason to reinvent the wheel ? – Franck Jul 26 '21 at 11:56

2 Answers2

2

You can use Json.NET's IContractResolver to get a JsonObjectContract for your Device object that contains information about all the properties of the type including their JSON names and get/set methods. The following extension method does the trick:

public static partial class JsonExtensions
{
    static readonly IContractResolver defaultResolver = JsonSerializer.CreateDefault().ContractResolver;

    public static void SetJsonProperty<T>(T obj, string jsonName, object value, bool exact = false, IContractResolver resolver = null)
    {
        if (!TrySetJsonProperty(obj, jsonName, value, exact, resolver))
            throw new ArgumentException(string.Format("Could not set property {0} in {1}.", jsonName, obj));
    }

    public static bool TrySetJsonProperty<T>(T obj, string jsonName, object value, bool exact = false, IContractResolver resolver = null)
    {
        if (obj == null)
            throw new ArgumentNullException(nameof(obj));
        resolver = resolver ?? defaultResolver;
        var contract = resolver.ResolveContract(obj.GetType()) as JsonObjectContract;
        if (contract == null)
            return false;
        var property = contract.Properties.GetProperty(jsonName, exact ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
        if (property == null || !property.Writable)
            return false;
        property.ValueProvider.SetValue(obj, value);
        return true;
    }
}

Furthermore, if you are reading from a text file and some of your property value types are not strings, you will need to convert the textual values from the file to the appropriate .Net type. The following does that:

public static partial class JsonExtensions
{
    public static void SetConvertibleJsonProperty<T, TConvertible>(T obj, string jsonName, TConvertible value, bool exact = false, IContractResolver resolver = null) where TConvertible : IConvertible
    {
        if (!TrySetConvertibleJsonProperty(obj, jsonName, value, exact, resolver))
            throw new ArgumentException(string.Format("Could not set property {0} in {1}.", jsonName, obj));
    }

    public static bool TrySetConvertibleJsonProperty<T, TConvertible>(T obj, string jsonName, TConvertible value, bool exact = false, IContractResolver resolver = null)
    {
        if (obj == null)
            throw new ArgumentNullException(nameof(obj));
        resolver = resolver ?? defaultResolver;
        var contract = resolver.ResolveContract(obj.GetType()) as JsonObjectContract;
        if (contract == null)
            return false;
        var property = contract.Properties.GetProperty(jsonName, exact ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
        if (property == null || !property.Writable)
            return false;
        var finalValue = value == null ? null : Convert.ChangeType(value, Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType, CultureInfo.InvariantCulture);
        property.ValueProvider.SetValue(obj, finalValue);
        return true;
    }
}

And to use it, do:

JsonExtensions.SetConvertibleJsonProperty(temp_obj, "ALLTYPES_NAME", "Object1");
JsonExtensions.SetConvertibleJsonProperty(temp_obj, "ALLTYPES_NAME", "Object2");
JsonExtensions.SetConvertibleJsonProperty(temp_obj, "INFORMATION", "Inside_Information");

// The following sets the following property
//    [JsonProperty("DECIMAL_DATA")] 
//    public decimal DecimalInformation { get; set; }
JsonExtensions.SetConvertibleJsonProperty(temp_obj, "DECIMAL_DATA", "3.1111");  

Notes:

Demo fiddle here.

dbc
  • 104,963
  • 20
  • 228
  • 340
0

After some experiments, based on the comments of Jon Skeet, I realise that I don't even need the JSON directives for this, as you can see from following code excerpt:

System.Reflection.PropertyInfo[] list_of_attributes = 
  (new Own_Class()).GetType().GetProperties();
string[] list_of_attribute_names = new string[list_of_attributes.Length];
for (int i = 0; i< list_of_attributes.Length; i++)
  list_of_attribute_names[i] = list_of_attributes[i].Name;
combobox_on_form.Items.AddRange(list_of_attribute_names);
Dominique
  • 16,450
  • 15
  • 56
  • 112