paiden / Nett

.Net library for TOML
MIT License
223 stars 27 forks source link

What is the best way given a TomlValue to get the string representation of it? #63

Closed AndrewSav closed 5 years ago

AndrewSav commented 6 years ago

I tired .Get but it's throwing different errors depending on the combination of value type and generic type.

Examples:

Console.WriteLine(Toml.ReadString("a=\"b\"")["a"].Get<object>());

Produces:

FormatException: Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).

And:

Console.WriteLine(Toml.ReadString("a=1979-05-27T07:32:00-08:00")["a"].Get<string>());

Produces:

InvalidOperationException: Cannot convert from type 'date time' to 'String'.
paiden commented 6 years ago

Your first example should be:

Console.WriteLine(Toml.ReadString("a=\"b\"")["a"].Get<string>());

I admit the behavior when using no conversion at all - as the type arg is object - is not very good. I'll check if I can improve that behavior in the future.

The TomlDateTimeOffset can not be converted to a string directly. This is by design, as I'm not a fan of implicit conversions as I was bitten by too many subtle bugs because of them in the past.

Your second example will work with the following modification:

Console.WriteLine(Toml.ReadString("a=1979-05-27T07:32:00-08:00")["a"].Get<DateTimeOffset>().ToString());
AndrewSav commented 6 years ago

@paiden Thank you for this, it does not answer the question though. I need a uniform way to print a string representation of a TomlValue. You gave two different ways, one of which won't work for dates and the other won't work for strings, which does not solve the problem.

Of course I can write a giant switch statement and write different conversions for each possible type, but that's usually what libraries are for. So... impossible to do it easily and naturally?

As a side note: error about guids when we doing nothing related to guids is not very good example of error reporting, this is something that can certainly be improved.

Also, I'd like to point out a difference between implicit and explicit conversion: explicit, is when you specify target type. Implicit is when you don't. In my example the target type is specified explicitly (string) which makes it explicit conversion, so that should work even by your definition. If you recon that explicit conversion could be provided by another method like GetExplicit<T> this is also a valid preference, but I do cannot find a method that resembles anything like that.

When you say bitten by too many subtle bugs, all you need is to make sure that there is not subtle bugs in you your code - which I think should be achievable, the users of your libraries should be free to shoot themselves in a leg in their code if they are given full information if they use explicit conversion it's up to them to implement all necessary checks - that's a standard practice more or less.

I would argue, that my examples above are a subtle bugs you are trying to avoid. I looked at the converter selection code and the first bug is direct result of selecting GUID converter for converting from TomlSting to object. There is also another string converter for converting from TomlString, but is not get chosen because the code uses linq FirstOrDefault and GUID happens to be first, Why GUID convertor presumes that it's better then a string one for converting TomlString to object is unexplainable other then a bug, so here you go.

For inspiration, I encourage you to look at how the famous json.net library is implemented. (Admittedly it does not have to deal with dates where you do):

Сonsole.WriteLine(JObject.Parse("{'a':'b'}")["a"]);
Console.WriteLine(JObject.Parse("{'a':123}")["a"]);

This just works, like it should be. Note that json.net also support custom converters, so it seems like a good fit if you need an inspiration.

In the end I would like wholeheartedly thank you for the hard work you've done on Toml support in .net; yours is one of the few libraries that took upon the task, and I readily acknowledge that it is by no means trivial - Json.Net for example contains almost 200000 lines of code. Keep up the great work! ;)

AndrewSav commented 6 years ago

An easy way to get what I want would be this:

Console.WriteLine(((dynamic)Toml.ReadString("a=\"b\"")["a"]).Value);
Console.WriteLine(((dynamic)Toml.ReadString("a=1979-05-27T07:32:00-08:00")["a"]).Value);

Unfortunately this incurs a heavy run-time penalty because of use of dynamic. Run-time dynamic dispatch is notorious for reduced performance. It has a distinct advantage of being simple/succinct though.

paiden commented 5 years ago

Fixed in 0.10.1 release