Skip to content

JSONSerialization: add WritingOptions.sortedKeys #1102

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

Merged
merged 4 commits into from
Jul 10, 2017

Conversation

ianpartridge
Copy link
Contributor

This implements the new sortedKeys constant in JSONSerialization.WritingOptions and adds some tests. This constant is documented at https://developer.apple.com/documentation/foundation/jsonserialization.writingoptions - it is new in the macOS 10.13 SDK.

This option is used by the new Swift 4 JSONEncoder which has been recently implemented.

Note: I am still doing performance testing on this to check it doesn't regress the non-sorted case, but I'm opening the PR for review and comment now.

CC: @bubski @itaiferber

@ianpartridge
Copy link
Contributor Author

By the way, @parkera: is there a reason that sortedKeys is listed under "Type Properties" but prettyPrinted is listed under "Constants" at https://developer.apple.com/documentation/foundation/jsonserialization.writingoptions ?

@pushkarnk
Copy link
Member

@swift-ci please test

@ianpartridge
Copy link
Contributor Author

Performance measurements of my first implementation showed ~8% cost in the default non-sorted case compared to the previous implementation, which is obviously not acceptable.

I've pushed another commit which eliminates this cost. It does make the code slightly more complex (there are now separate for-in loops for the sorted and non-sorted cases) but I don't think there is a way to avoid this.

@pushkarnk - please can you re-run the CI?

Copy link
Contributor

@itaiferber itaiferber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, save for the specifics of the string comparison

let b = b.key as? String else {
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.propertyListReadCorrupt.rawValue, userInfo: ["NSDebugDescription" : "NSDictionary key must be NSString"])
}
return a < b
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this will apply the exact same sorting strategy as we do on Darwin platforms. We are currently doing the equivalent of:

NSString * const key1 = ...
NSString * const key2 = ...
const NSRange range = NSMakeRange(0, key1.length);
NSLocale * const locale = [NSLocale systemLocale];
return [key1 compare:key2 options:NSNumericSearch | NSCaseInsensitiveSearch | NSForcedOrderingSearch range:range locale:locale];

Especially for archiving purposes, we should try to be consistent here.

@itaiferber
Copy link
Contributor

@ianpartridge The "Type Properties" vs. "Constants" thing looks like an artifact of the documentation generation system. Likely worth filing a Radar about that

let range = a.startIndex ..< a.endIndex
let locale = NSLocale.systemLocale()

return a.compare(b, options: options, range: range, locale: locale) == .orderedAscending
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review @itaiferber. Does this look right now?

Copy link
Contributor

@itaiferber itaiferber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@itaiferber
Copy link
Contributor

@swift-ci Please test

@phausler
Copy link
Contributor

phausler commented Jul 9, 2017

@swift-ci please test and merge

@ianpartridge
Copy link
Contributor Author

Build failed with

Foundation/NSJSONSerialization.swift:538:62: error: cannot convert value of type 'CountableRange<String.Index>' to expected argument type 'Range<String.Index>?'
                return a.compare(b, options: options, range: range, locale: locale) == .orderedAscending

I'll take a look tomorrow.

@ianpartridge
Copy link
Contributor Author

It looks like SE-0180 means that the compiler now infers let range = string.startIndex..<string.endIndex to be a CountableRange<String.Index> instead of Range<String.Index>.

As compare(_:options:range:locale:) requires a Range<String.Index>, I now specify this explicitly.

@phausler - can you try again please?

@phausler
Copy link
Contributor

@swift-ci please test and merge

@swift-ci swift-ci merged commit ef66189 into swiftlang:master Jul 10, 2017
@ianpartridge ianpartridge deleted the json-sortedkeys branch July 10, 2017 14:44
@parkera
Copy link
Contributor

parkera commented Jul 10, 2017

@ianpartridge The docs are updated independently of our headers, so we'll need a bug to fix those (Separate from all of this stuff of course).

@ianpartridge
Copy link
Contributor Author

Thanks @parkera - I've opened a Radar for the doc issue.

@itaiferber
Copy link
Contributor

@ianpartridge What's the Radar number? Want to make sure it gets to the right place. :)

@ianpartridge
Copy link
Contributor Author

33233023

@itaiferber
Copy link
Contributor

@ianpartridge Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants