Skip to content
Snippets Groups Projects
Commit 70898d7f authored by Dariusz Rybicki's avatar Dariusz Rybicki
Browse files

Merge branch 'feature/json-coders' into 'development'

Custom JSONEncoder & JSONDecoder

See merge request elixxir/elixxir-dapps-sdk-swift!53
parents 05995867 f5321cd6
No related branches found
No related tags found
2 merge requests!102Release 1.0.0,!53Custom JSONEncoder & JSONDecoder
import CustomDump
import Foundation
class JSONDecoder: Foundation.JSONDecoder {
override init() {
super.init()
}
override func decode<T>(_ type: T.Type, from data: Data) throws -> T where T: Decodable {
do {
let data = convertNumberToString(in: data, at: "Value")
return try super.decode(type, from: data)
} catch {
throw JSONDecodingError(error, data: data)
}
}
func convertNumberToString(
in input: Data,
at key: String
) -> Data {
guard var string = String(data: input, encoding: .utf8) else {
return input
}
string = string.replacingOccurrences(
of: #""\#(key)"( *):( *)([0-9]+)( *)(,*)"#,
with: #""\#(key)"$1:$2"$3"$4$5"#,
options: [.regularExpression]
)
guard let output = string.data(using: .utf8) else {
return input
}
return output
}
}
public struct JSONDecodingError: Error, CustomStringConvertible, CustomDumpReflectable {
public init(_ underlayingError: Error, data: Data) {
self.underlayingError = underlayingError
self.data = data
self.string = String(data: data, encoding: .utf8)
}
public var underlayingError: Error
public var data: Data
public var string: String?
public var description: String {
var description = ""
customDump(self, to: &description)
return description
}
public var customDumpMirror: Mirror {
Mirror(
self,
children: [
"underlayingError": underlayingError,
"data": String(data: data, encoding: .utf8) ?? data
],
displayStyle: .struct
)
}
}
import CustomDump
import Foundation
class JSONEncoder: Foundation.JSONEncoder {
override init() {
super.init()
}
override func encode<T>(_ value: T) throws -> Data where T: Encodable {
do {
var data = try super.encode(value)
data = convertStringToNumber(in: data, at: "Value")
return data
} catch {
throw JSONEncodingError(error, value: value)
}
}
func convertStringToNumber(
in input: Data,
at key: String
) -> Data {
guard var string = String(data: input, encoding: .utf8) else {
return input
}
string = string.replacingOccurrences(
of: #""\#(key)"( *):( *)"([0-9]+)"( *)(,*)"#,
with: #""\#(key)"$1:$2$3$4$5"#,
options: [.regularExpression]
)
guard let output = string.data(using: .utf8) else {
return input
}
return output
}
}
public struct JSONEncodingError: Error, CustomStringConvertible {
public init(_ underlayingError: Error, value: Any) {
self.underlayingError = underlayingError
self.value = value
}
public var underlayingError: Error
public var value: Any
public var description: String {
var description = ""
customDump(self, to: &description)
return description
}
}
import Foundation
/// Replaces all numbers at provided key with string equivalents
///
/// Example input:
/// {
/// "key": 123,
/// "object": {
/// "hello": "world",
/// "key": 321
/// }
/// }
///
/// Example output:
/// {
/// "key": "123",
/// "object": {
/// "hello": "world",
/// "key": "321"
/// }
/// }
///
/// - Parameters:
/// - input: JSON data
/// - key: the key which values should be converted
/// - Returns: JSON data
func convertJsonNumberToString(
in input: Data,
at key: String
) -> Data {
guard var string = String(data: input, encoding: .utf8) else {
return input
}
string = string.replacingOccurrences(
of: #""\#(key)"( *):( *)([0-9]+)( *)(,*)"#,
with: #""\#(key)"$1:$2"$3"$4$5"#,
options: [.regularExpression]
)
guard let output = string.data(using: .utf8) else {
return input
}
return output
}
/// Replaces all strings at provided key with number equivalents
///
/// Example input:
/// {
/// "key": "123",
/// "object": {
/// "hello": "world",
/// "key": "321"
/// }
/// }
///
/// Example output:
/// {
/// "key": 123,
/// "object": {
/// "hello": "world",
/// "key": 321
/// }
/// }
///
/// - Parameters:
/// - input: JSON data
/// - key: the key which values should be converted
/// - Returns: JSON data
func convertJsonStringToNumber(
in input: Data,
at key: String
) -> Data {
guard var string = String(data: input, encoding: .utf8) else {
return input
}
string = string.replacingOccurrences(
of: #""\#(key)"( *):( *)"([0-9]+)"( *)(,*)"#,
with: #""\#(key)"$1:$2$3$4$5"#,
options: [.regularExpression]
)
guard let output = string.data(using: .utf8) else {
return input
}
return output
}
...@@ -17,12 +17,10 @@ extension DHKey: Codable { ...@@ -17,12 +17,10 @@ extension DHKey: Codable {
} }
public static func decode(_ data: Data) throws -> Self { public static func decode(_ data: Data) throws -> Self {
let data = convertJsonNumberToString(in: data, at: "Value") try JSONDecoder().decode(Self.self, from: data)
return try JSONDecoder().decode(Self.self, from: data)
} }
public func encode() throws -> Data { public func encode() throws -> Data {
let data = try JSONEncoder().encode(self) try JSONEncoder().encode(self)
return convertJsonStringToNumber(in: data, at: "Value")
} }
} }
...@@ -17,24 +17,20 @@ extension GroupMember: Codable { ...@@ -17,24 +17,20 @@ extension GroupMember: Codable {
} }
public static func decode(_ data: Data) throws -> Self { public static func decode(_ data: Data) throws -> Self {
let data = convertJsonNumberToString(in: data, at: "Value") try JSONDecoder().decode(Self.self, from: data)
return try JSONDecoder().decode(Self.self, from: data)
} }
public func encode() throws -> Data { public func encode() throws -> Data {
let data = try JSONEncoder().encode(self) try JSONEncoder().encode(self)
return convertJsonStringToNumber(in: data, at: "Value")
} }
} }
extension Array where Element == GroupMember { extension Array where Element == GroupMember {
public static func decode(_ data: Data) throws -> Self { public static func decode(_ data: Data) throws -> Self {
let data = convertJsonNumberToString(in: data, at: "Value") try JSONDecoder().decode(Self.self, from: data)
return try JSONDecoder().decode(Self.self, from: data)
} }
public func encode() throws -> Data { public func encode() throws -> Data {
let data = try JSONEncoder().encode(self) try JSONEncoder().encode(self)
return convertJsonStringToNumber(in: data, at: "Value")
} }
} }
import CustomDump
import XCTest
@testable import XXClient
final class JSONDecoderTests: XCTestCase {
func testConvertingNumberToString() {
assertConvertingNumberToString(
input: #"{"number":1234567890,"text":"hello"}"#,
key: "number",
expectedOutput: #"{"number":"1234567890","text":"hello"}"#
)
assertConvertingNumberToString(
input: #"{"text":"hello","number":1234567890}"#,
key: "number",
expectedOutput: #"{"text":"hello","number":"1234567890"}"#
)
assertConvertingNumberToString(
input: #"{ "number" : 1234567890 , "text" : "hello" }"#,
key: "number",
expectedOutput: #"{ "number" : "1234567890" , "text" : "hello" }"#
)
assertConvertingNumberToString(
input: #"{ "text" : "hello" , "number" : 1234567890 }"#,
key: "number",
expectedOutput: #"{ "text" : "hello" , "number" : "1234567890" }"#
)
assertConvertingNumberToString(
input: """
{
"number": 1234567890,
"text": "hello"
}
""",
key: "number",
expectedOutput: """
{
"number": "1234567890",
"text": "hello"
}
"""
)
assertConvertingNumberToString(
input: """
{
"text": "hello",
"number": 1234567890
}
""",
key: "number",
expectedOutput: """
{
"text": "hello",
"number": "1234567890"
}
"""
)
}
}
private func assertConvertingNumberToString(
input: String,
key: String,
expectedOutput: String,
file: StaticString = #file,
line: UInt = #line
) {
XCTAssertNoDifference(
String(
data: JSONDecoder().convertNumberToString(
in: input.data(using: .utf8)!,
at: key
),
encoding: .utf8
)!,
expectedOutput,
file: file,
line: line
)
}
...@@ -2,91 +2,33 @@ import CustomDump ...@@ -2,91 +2,33 @@ import CustomDump
import XCTest import XCTest
@testable import XXClient @testable import XXClient
final class JSONHelpersTests: XCTestCase { final class JSONEncoderTests: XCTestCase {
func testConvertingNumberToStringByKey() {
assertConvertingJsonNumberToString(
input: #"{"number":1234567890,"text":"hello"}"#,
key: "number",
expected: #"{"number":"1234567890","text":"hello"}"#
)
assertConvertingJsonNumberToString(
input: #"{"text":"hello","number":1234567890}"#,
key: "number",
expected: #"{"text":"hello","number":"1234567890"}"#
)
assertConvertingJsonNumberToString(
input: #"{ "number" : 1234567890 , "text" : "hello" }"#,
key: "number",
expected: #"{ "number" : "1234567890" , "text" : "hello" }"#
)
assertConvertingJsonNumberToString(
input: #"{ "text" : "hello" , "number" : 1234567890 }"#,
key: "number",
expected: #"{ "text" : "hello" , "number" : "1234567890" }"#
)
assertConvertingJsonNumberToString(
input: """
{
"number": 1234567890,
"text": "hello"
}
""",
key: "number",
expected: """
{
"number": "1234567890",
"text": "hello"
}
"""
)
assertConvertingJsonNumberToString(
input: """
{
"text": "hello",
"number": 1234567890
}
""",
key: "number",
expected: """
{
"text": "hello",
"number": "1234567890"
}
"""
)
}
func testConvertingStringToNumber() { func testConvertingStringToNumber() {
assertConvertingJsonStringToNumber( assertConvertingStringToNumber(
input: #"{"number":"1234567890","text":"hello"}"#, input: #"{"number":"1234567890","text":"hello"}"#,
key: "number", key: "number",
expected: #"{"number":1234567890,"text":"hello"}"# expectedOutput: #"{"number":1234567890,"text":"hello"}"#
) )
assertConvertingJsonStringToNumber( assertConvertingStringToNumber(
input: #"{"text":"hello","number":"1234567890"}"#, input: #"{"text":"hello","number":"1234567890"}"#,
key: "number", key: "number",
expected: #"{"text":"hello","number":1234567890}"# expectedOutput: #"{"text":"hello","number":1234567890}"#
) )
assertConvertingJsonStringToNumber( assertConvertingStringToNumber(
input: #"{ "number" : "1234567890" , "text" : "hello" }"#, input: #"{ "number" : "1234567890" , "text" : "hello" }"#,
key: "number", key: "number",
expected: #"{ "number" : 1234567890 , "text" : "hello" }"# expectedOutput: #"{ "number" : 1234567890 , "text" : "hello" }"#
) )
assertConvertingJsonStringToNumber( assertConvertingStringToNumber(
input: #"{ "text" : "hello" , "number" : "1234567890" }"#, input: #"{ "text" : "hello" , "number" : "1234567890" }"#,
key: "number", key: "number",
expected: #"{ "text" : "hello" , "number" : 1234567890 }"# expectedOutput: #"{ "text" : "hello" , "number" : 1234567890 }"#
) )
assertConvertingJsonStringToNumber( assertConvertingStringToNumber(
input: """ input: """
{ {
"number": "1234567890", "number": "1234567890",
...@@ -94,7 +36,7 @@ final class JSONHelpersTests: XCTestCase { ...@@ -94,7 +36,7 @@ final class JSONHelpersTests: XCTestCase {
} }
""", """,
key: "number", key: "number",
expected: """ expectedOutput: """
{ {
"number": 1234567890, "number": 1234567890,
"text": "hello" "text": "hello"
...@@ -102,7 +44,7 @@ final class JSONHelpersTests: XCTestCase { ...@@ -102,7 +44,7 @@ final class JSONHelpersTests: XCTestCase {
""" """
) )
assertConvertingJsonStringToNumber( assertConvertingStringToNumber(
input: """ input: """
{ {
"text": "hello", "text": "hello",
...@@ -110,7 +52,7 @@ final class JSONHelpersTests: XCTestCase { ...@@ -110,7 +52,7 @@ final class JSONHelpersTests: XCTestCase {
} }
""", """,
key: "number", key: "number",
expected: """ expectedOutput: """
{ {
"text": "hello", "text": "hello",
"number": 1234567890 "number": 1234567890
...@@ -120,43 +62,22 @@ final class JSONHelpersTests: XCTestCase { ...@@ -120,43 +62,22 @@ final class JSONHelpersTests: XCTestCase {
} }
} }
private func assertConvertingJsonNumberToString( private func assertConvertingStringToNumber(
input: String,
key: String,
expected: String,
file: StaticString = #file,
line: UInt = #line
) {
XCTAssertNoDifference(
String(
data: convertJsonNumberToString(
in: input.data(using: .utf8)!,
at: key
),
encoding: .utf8
)!,
expected,
file: file,
line: line
)
}
private func assertConvertingJsonStringToNumber(
input: String, input: String,
key: String, key: String,
expected: String, expectedOutput: String,
file: StaticString = #file, file: StaticString = #file,
line: UInt = #line line: UInt = #line
) { ) {
XCTAssertNoDifference( XCTAssertNoDifference(
String( String(
data: convertJsonStringToNumber( data: JSONEncoder().convertStringToNumber(
in: input.data(using: .utf8)!, in: input.data(using: .utf8)!,
at: key at: key
), ),
encoding: .utf8 encoding: .utf8
)!, )!,
expected, expectedOutput,
file: file, file: file,
line: line line: line
) )
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment