Skip to content

Commit 353d762

Browse files
committed
[SR-2151]NSJSONSerialization.data produces illegal JSON code
NSJSONSerialization.data(withJSONObject:options) produces illegal JSON code https://bugs.swift.org/browse/SR-2151
1 parent b697c53 commit 353d762

File tree

3 files changed

+90
-2
lines changed

3 files changed

+90
-2
lines changed

Foundation/NSJSONSerialization.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ private struct JSONWriter {
306306
// Cannot detect type information (e.g. bool) as there is no objCType property on NSNumber in Swift
307307
// So, just print the number
308308

309-
writer("\(num)")
309+
writer(num.serializationString)
310310
}
311311

312312
mutating func serializeArray(_ array: NSArray) throws {

Foundation/NSNumber.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ open class NSNumber : NSValue {
139139
// This layout MUST be the same as CFNumber so that they are bridgeable
140140
private var _base = _CFInfo(typeID: CFNumberGetTypeID())
141141
private var _pad: UInt64 = 0
142-
143142
internal var _cfObject: CFType {
144143
return unsafeBitCast(self, to: CFType.self)
145144
}
@@ -451,8 +450,23 @@ open class NSNumber : NSValue {
451450
open override var description: String {
452451
return description(withLocale: nil)
453452
}
453+
454+
//[SR-2151] https://bugs.swift.org/browse/SR-2151
455+
internal var serializationString: String {
456+
let formatter: CFNumberFormatter
457+
formatter = CFNumberFormatterCreate(nil, CFLocaleCopyCurrent(), kCFNumberFormatterNoStyle)
458+
CFNumberFormatterSetProperty(formatter, kCFNumberFormatterMaxFractionDigits, 15._bridgeToObject())
459+
switch CFNumberGetType(_cfObject as CFNumber){
460+
case .floatType, .float32Type, .float64Type, .cgFloatType, .doubleType:
461+
CFNumberFormatterSetFormat(formatter, "0.###############"._cfObject);
462+
default:break
463+
}
464+
return CFNumberFormatterCreateStringWithNumber(nil, formatter, self._cfObject)._swiftObject
465+
}
454466
}
455467

468+
469+
456470
extension CFNumber : _NSBridgable {
457471
typealias NSType = NSNumber
458472
internal var _nsObject: NSType { return unsafeBitCast(self, to: NSType.self) }

TestFoundation/TestNSJSONSerialization.swift

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ extension TestNSJSONSerialization {
114114
("test_deserialize_badlyFormedArray", test_deserialize_badlyFormedArray),
115115
("test_deserialize_invalidEscapeSequence", test_deserialize_invalidEscapeSequence),
116116
("test_deserialize_unicodeMissingTrailingSurrogate", test_deserialize_unicodeMissingTrailingSurrogate),
117+
("test_serialize_dictionaryWithDecimal", test_serialize_dictionaryWithDecimal),
118+
117119
]
118120
}
119121

@@ -622,6 +624,78 @@ extension TestNSJSONSerialization {
622624
XCTAssertEqual(try trySerialize(array2), "[]")
623625
}
624626

627+
//[SR-2151] https://bugs.swift.org/browse/SR-2151
628+
//NSJSONSerialization.data(withJSONObject:options) produces illegal JSON code
629+
func test_serialize_dictionaryWithDecimal() {
630+
631+
//test serialize values less than 1 with maxFractionDigits = 15
632+
func excecute_testSetLessThanOne() {
633+
//expected : input to be serialized
634+
let params = [
635+
("0.1",0.1),
636+
("0.2",0.2),
637+
("0.3",0.3),
638+
("0.4",0.4),
639+
("0.5",0.5),
640+
("0.6",0.6),
641+
("0.7",0.7),
642+
("0.8",0.8),
643+
("0.9",0.9),
644+
("0.23456789012345",0.23456789012345),
645+
646+
("-0.1",-0.1),
647+
("-0.2",-0.2),
648+
("-0.3",-0.3),
649+
("-0.4",-0.4),
650+
("-0.5",-0.5),
651+
("-0.6",-0.6),
652+
("-0.7",-0.7),
653+
("-0.8",-0.8),
654+
("-0.9",-0.9),
655+
("-0.23456789012345",-0.23456789012345),
656+
]
657+
for param in params {
658+
let testDict = [param.0 : param.1 as AnyObject] as [String : AnyObject]
659+
let str = try? trySerialize(testDict.bridge())
660+
XCTAssertEqual(str!, "{\"\(param.0)\":\(param.1)}", "serialized value should have a decimal places and leading zero")
661+
}
662+
}
663+
//test serialize values grater than 1 with maxFractionDigits = 15
664+
func excecute_testSetGraterThanOne() {
665+
let paramsBove1 = [
666+
("1.1",1.1),
667+
("1.2",1.2),
668+
("1.23456789012345",1.23456789012345),
669+
("-1.1",-1.1),
670+
("-1.2",-1.2),
671+
("-1.23456789012345",-1.23456789012345),
672+
]
673+
for param in paramsBove1 {
674+
let testDict = [param.0 : param.1 as AnyObject] as [String : AnyObject]
675+
let str = try? trySerialize(testDict.bridge())
676+
XCTAssertEqual(str!, "{\"\(param.0)\":\(param.1)}", "serialized Double should have a decimal places and leading value")
677+
}
678+
}
679+
680+
//test serialize values for whole integer where the input is in Double format
681+
func excecute_testWholeNumbersWithDoubleAsInput() {
682+
683+
let paramsWholeNumbers = [
684+
("-1" ,-1.0),
685+
("0" ,0.0),
686+
("1" ,1.0),
687+
]
688+
for param in paramsWholeNumbers {
689+
let testDict = [param.0 : param.1 as AnyObject] as [String : AnyObject]
690+
let str = try? trySerialize(testDict.bridge())
691+
XCTAssertEqual(str!, "{\"\(param.0)\":\(param.0._bridgeToObject().intValue)}", "expect that serialized value should not contain trailing zero or decimal as they are whole numbers ")
692+
}
693+
}
694+
excecute_testSetLessThanOne()
695+
excecute_testSetGraterThanOne()
696+
excecute_testWholeNumbersWithDoubleAsInput()
697+
}
698+
625699
func test_serialize_null() {
626700
let arr = [NSNull()].bridge()
627701
XCTAssertEqual(try trySerialize(arr), "[null]")

0 commit comments

Comments
 (0)