andrewlock / StronglyTypedId

A Rosyln-powered generator for strongly-typed IDs
MIT License
1.52k stars 80 forks source link

Json serialization of longs to strings #70

Closed CheloXL closed 9 months ago

CheloXL commented 2 years ago

I know this is a very specific case, but... Javascript doesn't support longs. They are trimmed down to the nearest 53bit integer. I have a DB where Ids are longs so I implemented a custom json converter for my webapi that simply writes longs as strings and parse them back from strings (instead of using real js numbers).

I can't do the same with STI, as the serializer it generates simply does a writer.WriteNumberValue(value.Value);. That's fine inside the .net realm, but not in javascript.

I would like an option to be able to specify that longs should be [de]serialized as strings instead of numbers.

Would that be possible? Not sure about any other options that could solve the problem in a generic way.

jdrames commented 1 year ago

Wouldn't that concern be part of your application? I would assume something like AutoMapper to map from your domain db object to the DTO model where Id would be long StronglyTypedId backed value in your domain db entity and ID would then be a string in your DTO object.

[assembly: StronglyTypedIdDefaults(
    backingType: StronglyTypedIdBackingType.Long,
    converters: StronglyTypedIdConverter.SystemTextJson)]

...
[StronglyTypedId]
public partial struct ItemId { }

public class DbEntity
{
    public ItemId Id { get; set; }
    public string Name { get; set; }
}

public class DbEntityDTO
{
    public string Id { get; set; }
    public string Name { get; set; }
}
...
Mapper.CreateMap<DbEntity, DbEntityDTO>()
    .ForMember(dest=>dest.Id, opt=>opt.MapFrom(src=>Convert.ToString(src.Id)));

...
var source = new DbEntity{
   Id = new ItemId(420L),
   Name = "John Smith"
};

DbEntityDTO result = Mapper.Map<DbEntity, DbEntityDTO>(source);
CheloXL commented 1 year ago

If you have an anemic domain, you could. Or if your domain is 99% similar to your DTO. In my case, domain objects are not DB entities, and while I can have some properties in common between entity<->dto, I never map them using a mapper. At least not for commands. Queries, on the other hand, do get mapped, since those are usually quite similar to what I have in the DB.

jdrames commented 1 year ago

I guess I'm just lost on where the use case is for this. When displaying data out in an API I would use a dto object and simply convert the long to a string for the specified property. Same for incoming data via javascript. I would use an input dto, apply some fluent validation on that and also the json number handling attributes. I'd never map an incoming value directly to the domain object and use it to then do a command.

andrewlock commented 9 months ago

Sorry for the delay with this 😳. To be honest I got overwhelmed with how to handle the big range of feature requests and options. I think I've found an answer in the big redesign of the library in this PR:

The main idea is to make the library much more maintainable while also giving people a mechanism to customise the generated IDs as much as they like. This will make it easy to make changes like this, as you can simply provide a custom template that you can use which has the desired functionality 🙂