File: JSON%2BExtensions.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (138 lines) | stat: -rw-r--r-- 4,497 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2020-2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import struct Foundation.Data
import class Foundation.DateFormatter
import class Foundation.JSONDecoder
import class Foundation.JSONEncoder

#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension DateFormatter {
    public static let iso8601: DateFormatter = {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
        return dateFormatter
    }()
}

extension JSONEncoder.DateEncodingStrategy {
    public static let customISO8601 = custom {
        var container = $1.singleValueContainer()
        try container.encode(DateFormatter.iso8601.string(from: $0))
    }
}

extension JSONDecoder.DateDecodingStrategy {
    public static let customISO8601 = custom {
        let container = try $0.singleValueContainer()
        let string = try container.decode(String.self)
        if let date = DateFormatter.iso8601.date(from: string) {
            return date
        }
        throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date: \(string)")
    }
}
#endif

extension JSONEncoder.DateEncodingStrategy {
    public static var safeISO8601: JSONEncoder.DateEncodingStrategy {
        #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
        if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) {
            return .iso8601
        } else {
            return .customISO8601
        }
        #else
        return .iso8601
        #endif
    }
}

extension JSONDecoder.DateDecodingStrategy {
    public static var safeISO8601: JSONDecoder.DateDecodingStrategy {
        #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
        if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) {
            return .iso8601
        } else {
            return .customISO8601
        }
        #else
        return .iso8601
        #endif
    }
}

extension JSONDecoder {
    public static func makeWithDefaults(dateDecodingStrategy: DateDecodingStrategy = .safeISO8601) -> JSONDecoder {
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = dateDecodingStrategy
        return decoder
    }
}

extension JSONEncoder {
    public static func makeWithDefaults(
        prettified: Bool = true,
        dateEncodingStrategy: DateEncodingStrategy = .safeISO8601
    ) -> JSONEncoder {
        Self.makeWithDefaults(
            sortKeys: prettified,
            prettyPrint: prettified,
            escapeSlashes: !prettified,
            dateEncodingStrategy: dateEncodingStrategy
        )
    }

    public static func makeWithDefaults(
        sortKeys: Bool,
        prettyPrint: Bool,
        escapeSlashes: Bool,
        dateEncodingStrategy: DateEncodingStrategy = .safeISO8601
    ) -> JSONEncoder {
        let encoder = JSONEncoder()
        var outputFormatting: JSONEncoder.OutputFormatting = []

        if sortKeys {
            #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
            if #available(macOS 10.15, iOS 11.0, watchOS 4.0, tvOS 11.0, *) {
                outputFormatting.insert(.sortedKeys)
            }
            #else
            outputFormatting.insert(.sortedKeys)
            #endif
        }
        if prettyPrint {
            outputFormatting.insert(.prettyPrinted)
        }
        if !escapeSlashes {
            outputFormatting.insert(.withoutEscapingSlashes)
        }

        encoder.outputFormatting = outputFormatting
        encoder.dateEncodingStrategy = dateEncodingStrategy
        return encoder
    }
}

extension JSONDecoder {
    public func decode<T: Decodable>(path: AbsolutePath, fileSystem: FileSystem, as kind: T.Type) throws -> T {
        let data: Data = try fileSystem.readFileContents(path)
        return try self.decode(kind, from: data)
    }
}

extension JSONEncoder {
    public func encode<T: Encodable>(path: AbsolutePath, fileSystem: FileSystem, _ value: T) throws {
        let data = try self.encode(value)
        try fileSystem.writeFileContents(path, data: data)
    }
}