Skip to content

Update JSON usage from Any to JSONValue#740

Merged
mickael-menu merged 37 commits intoreadium:developfrom
stevenzeck:feature/json-value-migration
Mar 26, 2026
Merged

Update JSON usage from Any to JSONValue#740
mickael-menu merged 37 commits intoreadium:developfrom
stevenzeck:feature/json-value-migration

Conversation

@stevenzeck
Copy link
Copy Markdown
Contributor

This PR does the following:

  1. Moves JSON.swift out of ReadiumInternal and into ReadiumShared. It was also renamed to JSONDictionary to avoid conflicts with the existing JSON.swift in ReadiumShared.
  2. All uses of Any or [String: Any] were updated to use JSONValue.
  3. init(json: [String: Any]?) and the like were left for convenience, but ultimately use the new init in it.

Note this has breaking changes. I've drafted them in the Migration Guide.

Renamed to avoid conflicts with the existing JSON.swift file in Shared
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR migrates JSON handling across the Readium Swift codebase from untyped Any / [String: Any] to the type-safe JSONValue, and relocates/renames the JSONDictionary helper into ReadiumShared to support Equatable/Hashable JSON-backed models.

Changes:

  • Introduces JSONDictionary (now backed by [String: JSONValue]) plus updated parsing/encoding helpers, and removes the old internal JSON.swift.
  • Updates core publication models, parsers, and format sniffers to parse/serialize via JSONValue (with compatibility init(json: Any?) kept in many places).
  • Updates test fixtures and assertion helpers to work with JSONValue-based JSON structures and normalization.

Reviewed changes

Copilot reviewed 75 out of 75 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
Tests/StreamerTests/Parser/Image/ComicInfoParserTests.swift Adjusts test expectations for otherMetadata to use JSONValue accessors.
Tests/StreamerTests/Parser/EPUB/Services/EPUBPositionsServiceTests.swift Updates test helper properties to build JSONValue objects/arrays.
Tests/StreamerTests/Parser/EPUB/EPUBMetadataParserTests.swift Rewrites metadata fixtures to use .object/.array/.string JSONValue literals.
Tests/StreamerTests/Parser/EPUB/EPUBManifestParserTests.swift Rewrites manifest fixtures to use JSONValue.
Tests/StreamerTests/Asserts.swift Normalizes Any via JSONValue(...).any before JSONSerialization compare.
Tests/SharedTests/Publication/Services/Content/Iterators/HTMLResourceContentIteratorTests.swift Updates locator otherLocations test data to .string.
Tests/SharedTests/Publication/LocatorTests.swift Updates minimal JSON expectation to align with new encoding rules.
Tests/SharedTests/Publication/Extensions/OPDS/Properties+OPDSTests.swift Removes legacy [String: Any] cast in test fixture.
Tests/SharedTests/Publication/Extensions/HTML/Locator+HTMLTests.swift Removes legacy [String: Any] cast in test fixture.
Tests/SharedTests/Publication/Extensions/EPUB/Metadata+EPUBTests.swift Updates EPUBMediaOverlay JSON fixtures to new typed JSON forms.
Tests/SharedTests/Publication/Extensions/Archive/Properties+ArchiveTests.swift Removes legacy [String: Any] cast in test fixture.
Tests/SharedTests/Asserts.swift Updates file default and normalizes Any via JSONValue before compare.
Support/Carthage/Readium.xcodeproj/project.pbxproj Moves JSONDictionary.swift into Shared target sources; removes Internal JSON.swift.
Support/Carthage/.xcodegen Updates XcodeGen inputs to reflect JSONDictionary move/rename.
Sources/Streamer/Parser/Readium/ReadiumWebPubParser.swift Switches manifest decoding to asJSONObjectValue() and wraps into .object.
Sources/Streamer/Parser/Readium/ReadiumGuidedNavigationService.swift Switches JSON decoding to asJSONObjectValue() and wraps into .object.
Sources/Streamer/Parser/Image/ComicInfoParser.swift Encodes ComicInfo otherMetadata as [String: JSONValue] strings.
Sources/Streamer/Parser/EPUB/OPFParser.swift Encodes properties/encryption using JSONValue objects/arrays.
Sources/Streamer/Parser/EPUB/OPFMeta.swift Converts unknown metadata aggregation to [String: JSONValue].
Sources/Streamer/Parser/EPUB/EPUBMetadataParser.swift Wraps mediaOverlay JSON into .object in otherMetadata.
Sources/Shared/Toolkit/JSONDictionary.swift Adds new Shared JSONDictionary + parse/encode helper APIs using JSONValue.
Sources/Shared/Toolkit/JSON.swift Updates serialization + JSONEquatable to better interop with JSONValue.
Sources/Shared/Toolkit/Format/Sniffers/RWPMFormatSniffer.swift Uses asJSONObjectValue() and wraps into .object for Manifest init.
Sources/Shared/Toolkit/Format/Sniffers/RPFFormatSniffer.swift Uses asJSONObjectValue() and wraps into .object for Manifest init.
Sources/Shared/Toolkit/Format/Sniffers/OPDSFormatSniffer.swift Uses asJSONObjectValue() and wraps into .object for Manifest init.
Sources/Shared/Toolkit/Format/Sniffers/LCPLicenseFormatSniffer.swift Adjusts JSON reading path and guards with updated optional handling.
Sources/Shared/Toolkit/Format/Sniffers/JSONFormatSniffer.swift Updates to reflect readAsJSON() now yielding JSONValue?.
Sources/Shared/Toolkit/Format/FormatSnifferBlob.swift Changes cached JSON from Any? to JSONValue?.
Sources/Shared/Toolkit/Archive/ArchiveProperties.swift Migrates ArchiveProperties JSON parsing/encoding to JSONValue.
Sources/Shared/Publication/TDM.swift Migrates TDM JSON parsing/encoding to JSONValue with Any convenience init.
Sources/Shared/Publication/Subject.swift Migrates Subject parsing/encoding to JSONValue with Any convenience init.
Sources/Shared/Publication/Services/Positions/PositionsService.swift Emits/consumes positions JSON using JSONValue and asJSONObjectValue().
Sources/Shared/Publication/Services/Content/Iterators/HTMLResourceContentIterator.swift Makes locator otherLocations cssSelector explicitly typed JSONValue.
Sources/Shared/Publication/PublicationCollection.swift Migrates collection JSON parsing/encoding to JSONValue with Any convenience init.
Sources/Shared/Publication/Publication.swift Adds init(json: JSONValue) and bridges Any to JSONValue.
Sources/Shared/Publication/Properties.swift Migrates properties storage and subscript to JSONValue.
Sources/Shared/Publication/Metadata.swift Migrates metadata parsing/encoding to JSONValue with Any convenience init.
Sources/Shared/Publication/Manifest.swift Migrates manifest parsing/encoding to JSONValue with Any convenience init.
Sources/Shared/Publication/Locator.swift Migrates locator + nested types parsing/encoding to JSONValue with Any convenience inits.
Sources/Shared/Publication/LocalizedString.swift Migrates LocalizedString parsing/encoding to JSONValue with Any convenience init.
Sources/Shared/Publication/LinkRelation.swift Migrates LinkRelation array parsing to JSONValue with Any convenience init.
Sources/Shared/Publication/Link.swift Migrates Link parsing/encoding to JSONValue with Any convenience init.
Sources/Shared/Publication/GuidedNavigation/GuidedNavigationObject.swift Migrates guided navigation object parsing to JSONValue with Any convenience init.
Sources/Shared/Publication/GuidedNavigation/GuidedNavigationDocument.swift Migrates guided navigation document parsing to JSONValue with Any convenience init.
Sources/Shared/Publication/Extensions/Presentation/Properties+Presentation.swift Updates property access to JSONValue.bool.
Sources/Shared/Publication/Extensions/Presentation/Presentation.swift Migrates Presentation parsing/encoding to JSONValue with Any convenience init.
Sources/Shared/Publication/Extensions/HTML/Locator+HTML.swift Updates otherLocations access to JSONValue.string.
Sources/Shared/Publication/Extensions/HTML/DOMRange.swift Migrates DOMRange parsing/encoding to JSONValue with Any convenience init.
Sources/Shared/Publication/Extensions/Encryption/Encryption.swift Migrates Encryption parsing/encoding to JSONValue with Any convenience init.
Sources/Shared/Publication/Extensions/EPUB/EPUBMediaOverlay.swift Migrates EPUBMediaOverlay parsing/encoding to JSONValue with Any convenience init.
Sources/Shared/Publication/Extensions/Archive/Properties+Archive.swift Migrates archive properties parsing/encoding to JSONValue with Any convenience init.
Sources/Shared/Publication/Contributor.swift Migrates Contributor parsing/encoding to JSONValue with Any convenience init.
Sources/Shared/Publication/Accessibility/Accessibility.swift Migrates Accessibility parsing/encoding to JSONValue with Any convenience init.
Sources/Shared/OPDS/OPDSPrice.swift Migrates OPDSPrice parsing/encoding to JSONValue.
Sources/Shared/OPDS/OPDSHolds.swift Migrates OPDSHolds parsing to JSONValue-backed JSONDictionary.
Sources/Shared/OPDS/OPDSCopies.swift Migrates OPDSCopies parsing to JSONValue-backed JSONDictionary.
Sources/Shared/OPDS/OPDSAvailability.swift Migrates OPDSAvailability parsing to JSONValue-backed JSONDictionary.
Sources/Shared/OPDS/OPDSAcquisition.swift Migrates OPDSAcquisition encoding to JSONValue and adjusts parsing.
Sources/OPDS/OPDS1Parser.swift Emits OPDS link properties using [String: JSONValue].
Sources/Navigator/EPUB/HTMLDecorationTemplate.swift Migrates decoration template JSON output to [String: JSONValue].
Sources/LCP/License/Model/StatusDocument.swift Migrates LSD parsing to JSONValue-backed JSONDictionary and helpers.
Sources/LCP/License/Model/LicenseDocument.swift Migrates license document parsing to JSONValue-backed JSONDictionary and helpers.
Sources/LCP/License/Model/Components/Links.swift Adds JSONValue-based Links initializer; bridges legacy initializer.
Sources/LCP/License/Model/Components/Link.swift Adds JSONValue-based Link initializer; bridges legacy initializer.
Sources/LCP/License/Model/Components/LSD/PotentialRights.swift Adds JSONValue-based initializer; bridges legacy initializer.
Sources/LCP/License/Model/Components/LSD/Event.swift Adds JSONValue-based initializer; bridges legacy initializer.
Sources/LCP/License/Model/Components/LCP/UserKey.swift Adds JSONValue-based initializer; bridges legacy initializer.
Sources/LCP/License/Model/Components/LCP/User.swift Migrates extensions storage to [String: JSONValue] and JSONValue parsing.
Sources/LCP/License/Model/Components/LCP/Signature.swift Adds JSONValue-based initializer; bridges legacy initializer.
Sources/LCP/License/Model/Components/LCP/Rights.swift Migrates extensions storage to [String: JSONValue] and JSONValue parsing.
Sources/LCP/License/Model/Components/LCP/Encryption.swift Adds JSONValue-based initializer; bridges legacy initializer.
Sources/LCP/License/Model/Components/LCP/ContentKey.swift Adds JSONValue-based initializer; bridges legacy initializer.
Sources/Internal/JSON.swift Removes the old Internal JSONDictionary/parse helpers implementation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@stevenzeck
Copy link
Copy Markdown
Contributor Author

@mickael-menu I'll leave the remaining Copilot comments as unresolved in case you want to review the changes I made based on them.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 117 out of 117 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (4)

Sources/Shared/Toolkit/Format/Sniffers/JSONFormatSniffer.swift:1

  • json is not defined in this scope, so this closure will not compile. Return the mapped value (jsonValue) or the intended variable from the surrounding scope (if any) instead.
    Tests/SharedTests/Publication/LocatorTests.swift:1
  • XCTAssertEqual is not a throwing function, so prefixing it with try will fail compilation (“No throwing calls occur within 'try' expression”). Remove try from the XCTAssertEqual(...) call.
    Sources/Shared/Publication/LinkRelation.swift:1
  • init?(rawValue:) is failable but currently cannot fail, and init(_:) force-unwraps it. Make init(rawValue:) non-failable (or avoid the force unwrap in init(_:)) to remove an unnecessary crash path and better reflect the actual behavior.
    Sources/Shared/Publication/LinkRelation.swift:1
  • init?(rawValue:) is failable but currently cannot fail, and init(_:) force-unwraps it. Make init(rawValue:) non-failable (or avoid the force unwrap in init(_:)) to remove an unnecessary crash path and better reflect the actual behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Member

@mickael-menu mickael-menu left a comment

Choose a reason for hiding this comment

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

Thank you @stevenzeck!

@mickael-menu mickael-menu merged commit b97f532 into readium:develop Mar 26, 2026
5 checks passed
@mickael-menu mickael-menu deleted the feature/json-value-migration branch March 26, 2026 21:26
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.

3 participants