swift-extras-json icon indicating copy to clipboard operation
swift-extras-json copied to clipboard

Allow decoding Decimal from regular numbers

Open Cyberbeni opened this issue 6 years ago • 10 comments

Decimals are originally meant to be encoded/decoded as a dictionary: https://github.com/apple/swift/blob/3978d81c840c899bd090243d7d344f8dceacd74b/stdlib/public/Darwin/Foundation/Decimal.swift#L118 Apple's JSONDecoder provides a way to decode them from single numbers but sadly numbers are parsed as Double by JSONSerialization, so it loses precision: https://github.com/apple/swift/blob/3978d81c840c899bd090243d7d344f8dceacd74b/stdlib/public/Darwin/Foundation/JSONEncoder.swift#L2464

Since pure-swift-json parses numbers as string, we could provide a similar way of parsing Decimal from regular numbers. As far as I can understand, JSONSingleValueDecodingContainter would need a special decode function for Decimal.Type that would work similarly to the second link.

Decimal's init also takes a Locale, it should be allowed to be customized and the default should probably be "en_US".

Most likely a separate issue but there should also be an option for encoding Decimals the same way instead of using the default method.

Cyberbeni avatar Mar 19 '20 20:03 Cyberbeni

Related Swift bugreport: https://bugs.swift.org/browse/SR-7054

Cyberbeni avatar Mar 19 '20 21:03 Cyberbeni

Hi @Cyberbeni,

I get your point. Thanks for explaining here. Of course I could add a special decode function to JSONSingleValueDecodingContainter. The only problem is: You won't be able to access this function.

When you implement your own init(decoder:) and ask for a singleValueContainer() you will get a SingleValueDecodingContainer. You will not be able to cast this protocol to JSONSingleValueDecodingContainer because this is an implementation detail that should stay private and will not be exposed.

That's why I'm afraid the real solution to your problem is to propose a change to the Swift STL which would add a function decodeNumberAsString() to the SingleValueDecodingContainer protocol.

Does that make sense?

fabianfett avatar Mar 20 '20 11:03 fabianfett

I realized that you have to do it in the generic decode function and for all 3 types of containers.

Cyberbeni avatar Mar 20 '20 20:03 Cyberbeni

It‘s not so much a problem of my containers but the container protocol.

fabianfett avatar Mar 20 '20 20:03 fabianfett

func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
    if type.self == Decimal.Type,
         case let .number(number) = value {
        // Decode Decimal from String
    } else {
        // Decode normally
    }
}

And similarly for the other 2 containers.

Cyberbeni avatar Mar 20 '20 20:03 Cyberbeni

Okay. That would work, but in this case I would need to link Foundation - which kinda is not the goal of this Endeavour. But you‘re free to fork any time... @Cyberbeni Does this work for you?

fabianfett avatar Mar 20 '20 22:03 fabianfett

I also came here in hopes of finding a solution to the JSON->Decimal problem. This blog post is a good description of the issue in Swift.

xanderdunn avatar Oct 18 '20 23:10 xanderdunn

(I don't need this at my current job, that I also had when I opened this issue, the last 2 comments from March should lead you to a relatively easy solution @xanderdunn )

Cyberbeni avatar Oct 19 '20 20:10 Cyberbeni

Thanks @Cyberbeni, your solution was to fork the codebase and modify this func decode<T> to include a special case for Decimal?

xanderdunn avatar Oct 20 '20 02:10 xanderdunn

yes

Cyberbeni avatar Oct 20 '20 14:10 Cyberbeni