0

Newest Update w/ Answer

(10/26/2020) - Looking at @N.Dogac's solution I got a working solution reading this document https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to.

Answer can be seen here: https://github.com/RyanHarlich/My-Stackoverflow-Dotnet-Core-REST-API-Question/commit/0db8fa860eda7e9d201874f8f1abc38d168e0eaf.

Update

(10/2/2020) - I realized that reproducing this might be difficult and/or time consuming so I added a GitHub repo to focus on the actual problem; of course, this is only a sample and not my actual work so detail was left out. I only provided enough information to solve my question. Click here My GitHub Repo or click the actual link (https://github.com/RyanHarlich/My-Stackoverflow-Dotnet-Core-REST-API-Question)

If no one has a better solution I am going to try @N.Dogac's solution after awhile.

Question

Question: I was wondering how do I rename a JSONPropertyName with a client that uses a library in C# .NET Core REST API.

For example:

Library

Library DLL w/ MasterNode that enhances model detail

public abstract class APIModel {
public MasterNode Links { get; set; }

public void AddInfo(IUnitOfWork context) {
    Links = context.InheritedMasterNode;
    Links.Add();
}

public abstract class MasterNode {
    public abstract List<float> Info { get; set; }
    public void Add() {
        Info = new List<float>() { 100.0f };
    }
}
}

Model in Library DLL

public class Info : APIModel {
    public int id;
    public String title;
}

In the Library DLL contains the InfoController

[Route("api/[controller]")]
    [ApiController]
    public class InfosController : ControllerBase {
        private readonly IUnitOfWork _unitOfWork;

        public InfosController(IUnitOfWork context) {
            _unitOfWork = context;
        }

        // GET: api/Infos
        [HttpGet]
        public async Task<ActionResult<IEnumerable<Models.HyperMediaControls.APIBase>>> GetAPIs() {
            ResetInfo().Wait();
            var conn = _unitOfWork.DBContext.Database.GetDbConnection();
            if (conn.State != ConnectionState.Open)
                conn.Open();

            _unitOfWork.DBContext.Database.ExecuteSqlRaw("SET IDENTITY_INSERT Info ON");
            Info info = new Library.Models.Info() { Id = 1 };
            info.Title = "Info";
            await _unitOfWork.Repository<Info>().InsertEntityAsync(info);

            await _unitOfWork.SaveChangesAsync();
            conn.Close();

            Add(info);

            return await _unitOfWork.Repository<Info>().ToListAsync();
        }

        private async Task ResetInfo() {
            Info info = await _unitOfWork.Repository<Info>().FirstOrDefault();
            if (info != null) {
                _unitOfWork.Repository<Info>().Empty(info);
                await _unitOfWork.SaveChangesAsync();
            }
        }

        public void Add(Models.HyperMediaControls.APIBase info) {
            info.AddInfo(, _unitOfWork);
        }
    }

Client

Client: [JsonPropertyName(“InfoInherited”)] is what I am trying to achieve

public abstract class InheritedMasterNode : MasterNode {
    [JsonPropertyName(“InfoInherited”)]
    public override List<float> Info { get; set; } = null;
}

Details

To get the child InheritedMasterNode from client to library I used this method, which I did not add as it is a lot of code for a Stack Overflow post: https://stackoverflow.com/a/59753787/8360201

  • Note: You use the IUnitOfWork to inject the InheritedMasterNode into a MasterNode handler and use it in the APIModel class with the property InheritedMasterNode of UnitOfWork.

The problem that I am facing is as described in my comments here: https://stackoverflow.com/a/37927869/8360201

  • Note: The answer to this Stack Overflow post does not work because it serializes to a string; where as, I use C# .NET Core REST API Controllers.

Please let me know what you need in order for me to help you reproduce this. I left out a lot of code as it is my private repo and it would be too much to read to understand the smaller problem.

Overview:

  • Belongs to Library DLL: Info, InfoController, APIModel, MasterNode, UnitOfWork, ServiceCollectionExtensions
  • Belongs to Client: InheritedMasterNode, Startup.ConfigureService

I have come to the conclusion that it is not possible and would require the .NET Core team to do an upgrade to these DLL, but I am giving it a final try by asking the Stack Overflow community:

  • using Microsoft.AspNetCore.Mvc;
  • using Microsoft.EntityFrameworkCore;

Here is the actual output vs. desired: info -> infoInherited

Actual Output:

[
  {
    "id": 1,
    "title": "Info",
    "links": {
      "info": [
        ]
     }
  }
]

Desired Output:

[
  {
    "id": 1,
    "title": " Info ",
    "links": {
      "infoInherited": [
        ]
     }
  }
]
Ryan Harlich
  • 157
  • 1
  • 7
  • 1
    The property names or attributes in those DLLs are metadata, these are loaded by .NET runtime and CLR, and as far as I know, you cannot change them. Of course, there will be some tricks but they are very unsafe. – ndogac Sep 29 '20 at 21:28
  • @N.Dogac What would be unsafe about it? I might be interested in these depending on what is unsafe about it. – Ryan Harlich Sep 29 '20 at 21:38
  • Many classes or methods can depend on their names and attributes, changing them will cause many exceptions and strange behavior. By the way, you can try to change them with hex editor, find their names in binary format and replace them with your names. – ndogac Sep 29 '20 at 21:44
  • Open your .dll file with this https://www.onlinehexeditor.com/ and search for your attributes. – ndogac Sep 29 '20 at 21:45
  • @N.Dogac It would have to be done in C# with the the client and also dynamically. The hex editor seems like it could be a possibility, but I'd imagine the solution would be slow to process or time consuming if I have 1000 microservices using the library wanting different names and I make a revision to the library often, as it is my library. – Ryan Harlich Sep 29 '20 at 21:58
  • Is this really just about renaming a when you serialize or deserialize JSON? If so, what in the world does that have to do Entity Framework or things like the unit of work pattern? If not, please explain how all these things are related. – Aluan Haddad Oct 02 '20 at 19:46
  • @AluanHaddad I am trying to make many microservices, lets say 1000. If all these microservices use something similar like an infos controller that displays all the data in the database for that microservice, then I don't want to have to create that same thing 1000 times and have a drift pattern. It would be best to create a library to have an infos controller that you can feed from the client its context to the library to enhance the JSON output. Does that explain it, or do you need me to elaborate? – Ryan Harlich Oct 02 '20 at 23:15
  • Also, if I didn't do it this way I'd have spaghetti code. – Ryan Harlich Oct 02 '20 at 23:17
  • It sounds like you just want a custom `JsonConverter` – Aluan Haddad Oct 03 '20 at 00:26
  • @AluanHaddad Do you know what the controllers are? I don't think I have control of the JSON of a controller. – Ryan Harlich Oct 03 '20 at 04:25
  • If you have control over startup or the dependency injection system you can plug it in there. – Aluan Haddad Oct 03 '20 at 07:06
  • 1
    @AluanHaddad Are you referring to this line: "services.AddMvc().AddJsonOptions(options => ...);"? I think I already tried to do that and there was not an option to plug in the JsonConverter. If it is possible, could you supply an answer? – Ryan Harlich Oct 03 '20 at 16:40

1 Answers1

1

You should be able to achieve this by registering a Json converter for the type in question

services.AddMvc()
    .AddJsonOptions(options =>
    {
         options.SerializerSettings.Converters.Add(new  APIModelJsonConverter());
     });

class APIModelJsonConverter: JsonConverter<APIModel> {
    public override void Write(Utf8JsonWriter writer, APIModel m, JsonSerializerOptions options) {
        var intermediateJson = JsonSerializer.Serialize(m, options);
        var intermediateObject = JsonSerializer.Deserialize<Dictionary<string, object>>(intermediateJson, options);
         intermediateObject["infoInherited"] = intermediateObject["info"];
         intermediate.Remove("info");

         writer.Write(intermediate, options);
    }
} 
Aluan Haddad
  • 29,886
  • 8
  • 72
  • 84
  • I got the solution to build and run, but not seeing the effect take place. Did you run this with the GitHub solution I provided? – Ryan Harlich Oct 05 '20 at 13:57
  • Did you get a chance to try it out with the solution? I will dig into this in October-November if you have not and give you suggestions for me to accept this answer. – Ryan Harlich Oct 11 '20 at 15:38
  • Sorry I've been busy – Aluan Haddad Oct 12 '20 at 22:27
  • Hi Aluan, sorry about that. My laptop overheated, so I was only able to have a glance at it before and I thought I saw something that would make it not feasible. I finally got a new laptop and got a chance to look at it today. Was in need for an updated laptop as mine was a few gens older. You can see my update in the question for the answer. While yours is not quite right, I'll mark it correct as it pointed in the right direction and it would be difficult to write answer on Stack Overflow. I posted a link to my git commit with changes in my update in my question. Thank you very much! – Ryan Harlich Oct 26 '20 at 18:06