-
-
Notifications
You must be signed in to change notification settings - Fork 165
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
sortKeys: Boolean
flag to ujson/upickle APIs (#553)
This PR allows anyone writing JSON to pass in `sortKeys = true` to make the object keys get output in sorted order. ## Major changes: 1. Introduced a new `upickle.core.BufferedValue` type. This is a sealed trait hierarchy that can be used to capture all the method calls received by a `upickle.core.Visitor`, and re-play them later. It replaces `IndexedValue`, which has JSON-specific idiosyncracies (e.g. converting MsgPack visitor calls to their JSON equivalents) whereas `BufferedValue` precisely captures the `Visitor` method calls unchanged. `IndexedValue` is left for binary compatibility but no longer used internally, even its ordering use case of buffering up things until a `$type` tag is found has been replaced by `BufferedValue` 2. As part of the generalization from `IndexedValue` to `BufferedValue`, we now need to handle non-JSON usage patterns such as non-string-keyed objects (e.g. found in msgpack). This makes sorting these objects non-trivial, as their keys can now be arbitrary `BufferedValue`s. `BufferedValue.valueToSortKey` is a rough hack to traverse any structured keys and make them sortable. 3. Intercept all `transform` calls in the primary upickle/ujson APIs with `BufferedValue.maybeSortKeysTransform`. This checks the new `sortKeys` boolean flag, and if true it intercepts the visitor calls, captures them in a `BufferedValue`, recursively traverses and sorts the `BufferedValue`'s object keys, then re-plays it back out 4. Binary compatibility stubs have been added to all methods who received a new `sortKeys: Boolean = true` parameter. These stubs were not marked as `@Deprecated`, because in some cases the Scala compiler would preferentially resolve the stub over the method-with-default-value, causing spurious deprecation warnings. ## Testing 1. Sorting functionality is covered by new `sortKeys` unit tests in uJson and uPickle, and has been added to the `TestUtil.scala` round-trip tests to ensure that all existing tests exercise the a subset of the `sortKeys = true` codepaths for JSON and MsgPack and verify that the round-tripping is still successful ## Notes 1. It took a surprising amount of code to make this work well, as it goes against uPickle's "streaming-no-intermediate-data-structures" way of doing things. `sortKeys = true` *has* to buffer up some kind of intermediate JSON AST somewhere. I would expect performance to be worse than the default zero-intermediate-datastructures visitor approach, and so optimizing the `sortKeys = true` code paths was not a priority in this PR. If anyone wants maximum performance, they can leave it as the default `sortKeys = false` 2. The whole `Transformer`/`transform` abstraction in the codebase is kind of half-baked and unsatisfactory, but I left it mostly as is for now with only small tweaks to make things work (e.g. allowing the implementation of `BufferedValue.maybeSortKeysTransform` to live in `upickle.core`). Realistically, the whole name `Transformer` and the concept it represents probably needs to be overhauled, but that's out of scope for this PR 3. `TestUtil.scala` is getting big and messy, and I'm adding to the mess. It's probably OK to leave for now, but it could definitely use a cleanup
- Loading branch information
Showing
17 changed files
with
503 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,6 @@ | ||
package ujson | ||
import upickle.core.Visitor | ||
|
||
trait Transformer[I] { | ||
def transform[T](j: I, f: Visitor[_, T]): T | ||
trait Transformer[I] extends upickle.core.Transformer[I]{ | ||
def transformable[T](j: I) = Readable.fromTransformer(j, this) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package upickle.core.compat | ||
|
||
object SortInPlace { | ||
def apply[T, B: Ordering](t: collection.mutable.ArrayBuffer[T])(f: T => B): Unit = { | ||
val sorted = t.sortBy(f) | ||
t.clear() | ||
t.appendAll(sorted) | ||
} | ||
} |
2 changes: 1 addition & 1 deletion
2
...3+/upickle/core/LinkedHashMapCompat.scala → ...kle/core/compat/LinkedHashMapCompat.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package upickle.core.compat | ||
|
||
object SortInPlace { | ||
def apply[T, B: scala.Ordering](t: collection.mutable.ArrayBuffer[T])(f: T => B): Unit = { | ||
t.sortInPlaceBy(f) | ||
} | ||
} |
Oops, something went wrong.