How to transform an object during conversion to json?

I have a flat DTO object with ~500 fields that is returned via an API call. The .NET machinery converts it to JSON and all is good. It looks like this:

{
  "id": 1,
  "desc": "foo",
  "system": "axmls",
  "bedrooms": 5,
  "flooring": "tile",
  "roof": "tesla",
  ...
  ...
}

Now I am being asked to place these ~500 fields into categories to make it look similar to this:
{
  "main": {
    "id": 1,
    "desc": "foo",
    "system": "axmls",
    ...
  }
  "interior": {
    "bedrooms": 5,
    "flooring": "tile",
    ...
  }
  "exterior": {
    "roof": "tesla",
    ...
  }
}

I’ve thought of couple of ways to doing this (none too enticing)
  1. Create a new object to match this structure and copy data to it. Not great.
  2. Build JSON by hand. No.
  3. Mark each field on the original object with a Category attribute and then somehow in a FilterAttribute transform it. This one sounds most promising, though I am not sure how to implement it.

Is there a way to create a JSON structure like this in a sane manner?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

Here’s what I described in the comments. First, you need some metadata. I use an enum here (since it makes typos less likely – though it does make the code ever so slightly more complicated.

(UPDATE: I found a bug, I was treating everything as strings. now I treat the leaf nodes of the trees as object)

First the category types:

private enum CategoryName
{
    Main,
    Interior,
    Exterior,
}

and the category metadata:
private static readonly Dictionary<string, CategoryName> _categoryMetadata =
    new Dictionary<string, CategoryName>
    {
        {"id", CategoryName.Main},
        {"desc", CategoryName.Main},
        {"system", CategoryName.Main},
        {"bedrooms", CategoryName.Interior},
        {"flooring", CategoryName.Interior},
        {"roof", CategoryName.Exterior}
    };

Now, your input JSON. Note that I single-quoted the strings, it makes it easier to use in C# and doesn’t change the nature of the JSON. I also took out all your ... entries:
 private const string InputJson = @"
 {
     'id': 1,
     'desc': 'foo',
     'system': 'axmls',
     'bedrooms': 5,
     'flooring': 'tile',
     'roof': 'tesla',
 }";

Finally the code. As I described in the comments “Build a metadata dictionary (string, string) that maps each item (bedrooms, flooring, roof) to categories (main, interior, exterior). Read the data you have (the big initial list) into a Dictionary<string, string>. Then walk that Dictionary, looking up the category in the metadata. Finally populate a Dictionary<string, Dictionary<string, string>> with the results and serialize it back out to JSON”:
var output = new Dictionary<string, Dictionary<string, object>>();
var input = JsonConvert.DeserializeObject<Dictionary<string, object>>(InputJson);
foreach (var item in input)
{
    if (_categoryMetadata.TryGetValue(item.Key, out var category))
    {
        var categoryString = category.ToString().ToLower();
        if (!output.ContainsKey(categoryString))
        {
            output[categoryString] = new Dictionary<string, object>();
        }

        output[categoryString].Add(item.Key, item.Value);
    }
}

var result = JsonConvert.SerializeObject(output, Formatting.Indented);

When that’s finished (and after my update), the JSON looks like:
{
  "main": {
    "id": 1,
    "desc": "foo",
    "system": "axmls"
  },
  "interior": {
    "bedrooms": 5,
    "flooring": "tile"
  },
  "exterior": {
    "roof": "tesla"
  }
}

In real life, you’d remove the Formatting.Indented parameter on the JsonConvert.Serialize call.

Method 2

Just to visualise how simple it can look 🙂

public class House
{
    public int Id { get; set; }
    public string Description { get; set; }
    public string System { get; set; }
    public int Bedrooms { get; set; }
    public string Flooring { get; set; }
    public string Roof { get; set; }
}

Publicly exposed structure without copying values
public class ExposedHouse
{        
    public Main Main { get; }
    public Interior Interior { get; }
    public Exterior Exterior { get; }

    public ExposedHouse(House house)
    {
        Main = new Main(house);
        Interior = new Interior(house);
        Exterior = new Exterior (house);
    }

    public class Main
    {
        private readonly House _house;

        public int Id => _house.Id;
        public string Description => _house.Description;
        public string System => _house.System;
   
        public Main(House house) { _house = house; }
    }

    public class Interior
    {
        private readonly House _house;

        public int Bedrooms => _house.Bedrooms;
        public string Flooring => _house.Flooring;

        public Interior(House house) { _house = house; }
    }

    public class Exterior
    {
        private readonly House _house;

        public string Roof => _house.Flooring;

        public Exterior(House house) { _house = house; }
    }
}

And conversion without copying values
var exposed = new ExposedHouse(house);
return Ok(exposed);

If requirements change in the future and you need to format some values differently, with this approach anybody will be able to do it 🙂


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x