-
Notifications
You must be signed in to change notification settings - Fork 1.2k
JSONEncoder implementation #1048
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@swift-ci please test |
Thanks for taking this on! |
I'll be able to review this more in depth later today — thanks for bringing this over to Linux! At first glance, looks reasonable to me. |
@phausler What do you think about the numeric casting abstractions here? |
Foundation.xcodeproj/project.pbxproj
Outdated
@@ -2778,7 +2808,7 @@ | |||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; | |||
LIBRARY_SEARCH_PATHS = "$(inherited)"; | |||
MACH_O_TYPE = mh_execute; | |||
OTHER_SWIFT_FLAGS = "-DDEPLOYMENT_ENABLE_LIBDISPATCH -swift-version 3"; | |||
OTHER_SWIFT_FLAGS = "-DDEPLOYMENT_ENABLE_LIBDISPATCH -swift-version 3 -DTEST_TARGET"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need to add this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So that we import Foundation
/import SwiftFoundation
here
https://github.com/apple/swift-corelibs-foundation/pull/1048/files#diff-fa28084658370ec9069bda50577d8ab1R14
only when build for TestFoundation
platformConsistentCast
is internal, I cannot expose it to TestFoundation, so needed to add this source file to TestFoundation target.
Foundation/ConsistentCasting.swift
Outdated
return nil | ||
} | ||
|
||
protocol _PlatformConsistentCasting { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be encompassed in the Bridging.swift, also protocols are a bit slippery for scope - we should annotate this as internal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@phausler could you say a bit more why protocols
are slippery for scope?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we have had issues where declarations accidentally leak out beyond the module of Foundation (however I haven't seen that since).
Even though internal is the default application of scope, it just seems to be good hygiene to make certain they are marked as such.
Foundation/ConsistentCasting.swift
Outdated
} | ||
} | ||
|
||
extension Int8: _PlatformConsistentCasting { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should pull the implementation from NSNumber.swift from the overlay to make it have parity
Overall this looks like the right direction, however I think we should try and figure out a consistent way of implementing the casting via using the exactly or the bridge methods. I don't think it is out of scope to potentially change the JSONEncoder/JSONDecoder on Darwin to match if we can figure out a good way of implementing it so that we have parity for numeric types. |
@phausler Thanks for the review. |
let me know if you need any help; I think we can accomplish this pretty easily via the _SwiftValue.fetch method which simulates |
I can't really see how casting would benefit from using From what I see, Also, And, if I'm not mistaken, I'd appreciate any guidance here. |
So the problem is going to be recursive type conversions; e.g. Dictionaries that contain other collections. We will always have some mismatch of parity of some of the elements but I think we can get a 90% use case solution here with Here is the idea: Since the numeric type accessors have type (bound to the bridgeable type) being sent in we can construct the requested type via the bridge method _conditionallyBridgeFromObjectiveC etc. That way when client code requests a Double or an Int8 they get the expected behavior. This also means that we need to bring across the updated NSNumber.swift bridge in the overlay (which the entire intent of changing how that was bridged was done so specifically for Codable support) The long-tail advantage here is that we can likely get better behavior out of JSONSerialization or PropertyListSerialization for developers by going down this route; ideally we should share as much code and behavior between the Darwin versions and the swift-corelibs version as possible. If it would help I can knock out a rough draft of bringing the NSNumber changes over. I think that going down that route will aide ensuring the JSONEncoder to be as close as possible. |
@parkera What is your weigh in on this? I know we have limited working schedule for things but I think it is pretty important that we get this right. |
@phausler right, recursion would break… As for @phausler could you help me out and explain what |
@bubski I discussed this with @phausler some more and we had an idea which I think will simplify this PR quite a bit by removing the need for the casting. We can add an internal-only entry point to Then, when the encoder/decoder would have called The reason we decided to return the Swift numeric types from Taking a quick look at the JSON decoder, I think this option would be as simple as passing another option through to the Hopefully this makes sense. Please let me know if you have any questions. |
@parkera Yes, this makes sense and sounds like a plan 👍 I'll get around to it. Is casting to/from |
I'd love to support using |
@parkera Followed your advise and pushed changes. |
Foundation/NSJSONSerialization.swift
Outdated
@@ -23,6 +23,7 @@ extension JSONSerialization { | |||
public static let mutableContainers = ReadingOptions(rawValue: 1 << 0) | |||
public static let mutableLeaves = ReadingOptions(rawValue: 1 << 1) | |||
public static let allowFragments = ReadingOptions(rawValue: 1 << 2) | |||
internal static let useReferenceNumericTypes = ReadingOptions(rawValue: 1 << 3) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it might be a good idea to use some other value than 1 << 3
since it would be quite likely that we could add a public value that uses that definition.
my suggestion is use a higher value...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How high would you recommend?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like 1 << 12
or 1 << 15
should suffice
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're unlikely to need to add that many options; in the future, we can modify this value if need be, since it's just internal here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👌 done
…nto json-encoder # Conflicts: # Foundation.xcodeproj/project.pbxproj # Foundation/Data.swift
As a follow up task; I know Itai is working on some other additional changes to the encoders. We should figure out a way to be able to copy/paste the code easily between the two implementations. That way when changes are made in one location we can just do a simple duplication instead of needing to re-port the changes over again. |
@itaiferber @phausler should I make any more changes in this PR? |
I think it looks good as a starting point, lets revisit the follow up task once Itai lands some of the other updates. I will let Itai do the final review and merge. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM overall! We'll probably want to start porting over some Codable
implementations from other Foundation types (swiftlang/swift#9715, swiftlang/swift#10404).
Also, I'm not sure how JSONSerialization
handles Decimal
if at all, but we'll probably need to apply swiftlang/swift#10553 here too.
@phausler Good to merge, though; these changes can come in future PRs.
|
||
// Adding the following extensions to EncodingError and DecodingError allows them to bridge to NSErrors implicitly. | ||
|
||
fileprivate let NSCodingPathErrorKey = "NSCodingPath" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we don't have error bridging in Linux, I think this whole section (short of the helper methods on DecodingError
) can be removed.
Thanks @itaiferber and @phausler. I'll continue working around the things you mentioned in coming PRs. |
@swift-ci test and merge |
Hi @bubski - thank you so much for working on this! Do you have any news on adding |
@ianpartridge credit goes to @itaiferber . He's done all the heavy lifting. Yes, I'm porting other types to Here's one: #1092 My approach in #1092 might not be the best though. I know that @phausler is reworking some NSCF stuff which might solve this problem as well. |
Sounds great - glad to hear you're making progress. Is there a public branch you're pushing to, so I can follow along? |
Sure @ianpartridge https://github.com/bubski/swift-corelibs-foundation/tree/codable |
Resolves: https://bugs.swift.org/browse/SR-5195
This PR mirrors
JSONEncoder
implementation from SDK for Darwin, so that it's available on Linux.swiftlang/swift#9005
platformConsistentCast
There are two major discrepancies between macOS and Linux.
JSONSerialization
uses onlyNSNumber
to represent numbers in json objects. On Linux however, standard Swift types likeDouble
orInt
are used.NSNumber
andSwift numeric types
behaves very differently on macOS and Linux.To keep matters consistent, I use
func platformConsistentCast
inJSONEncoder.swift
instead ofas
until the above issues are resolved.New swift compiler flag
-DTEST_TARGET
This flag is used when compiling TestFoundation sources.
Because
@testable
doesn't work on Linux, I had to addConsistentCasting.swift
to both Foundation and TestFoundation targets, to expose internalfunc platformConsistentCast
for tests.-DTEST_TARGET
is used to onlyimport Foundation
inConsistentCasting.swift
if it's compiled as a part of TestFoundation target.Mirrored files (with slight changes to resolve compilation errors on Linux):
JSONEncoder.swift
from:https://github.com/apple/swift/blob/c5ff1f2cac8da6a14330f4b033b94c7c926d2126/stdlib/public/SDK/Foundation/JSONEncoder.swift
Codable.swift
from:https://github.com/apple/swift/blob/012ea9373b5d2f72c59e16e18bd077c3ee86c745/stdlib/public/SDK/Foundation/Codable.swift
Data.swift
(Data : Codable
implementation) from:https://github.com/apple/swift/blob/c40ba96328129d322a41afbd018b2a4081409e5e/stdlib/public/SDK/Foundation/Data.swift#L1759
Date.swift
(Date : Codable
implementation) from:https://github.com/apple/swift/blob/012ea9373b5d2f72c59e16e18bd077c3ee86c745/stdlib/public/SDK/Foundation/Date.swift#L288
URL.swift
(URL: Codable
implementation) from:https://github.com/apple/swift/blob/c5ff1f2cac8da6a14330f4b033b94c7c926d2126/stdlib/public/SDK/Foundation/URL.swift#L1210
NSError.swift
(new error codes) from:https://github.com/apple/swift/pull/9005/files#diff-a25f757c0d5c775e5b48e180b4a24efa