Skip to content

Commit d0b475b

Browse files
authored
fix a problem that conversion from a large number like "132009969506862891" is failed (#727)
* fix a problem that conversion from a large number like "132009969506862891" is failed * Add more unit test cases Refactor the core conversion if-else structure Add signed & Unsigned support
1 parent 88f2519 commit d0b475b

File tree

4 files changed

+100
-58
lines changed

4 files changed

+100
-58
lines changed

MJExtension/NSObject+MJKeyValue.m

Lines changed: 62 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,26 @@
1616
#import "NSString+MJExtension.h"
1717
#import "NSObject+MJClass.h"
1818

19+
@implementation NSDecimalNumber(MJKeyValue)
20+
21+
- (id)standardValueWithTypeCode:(NSString *)typeCode {
22+
// 由于这里涉及到编译器问题, 暂时保留 Long, 实际上在 64 位系统上, 这 2 个精度范围相同,
23+
// 32 位略有不同, 其余都可使用 Double 进行强转不丢失精度
24+
if ([typeCode isEqualToString:MJPropertyTypeLongLong]) {
25+
return @(self.longLongValue);
26+
} else if ([typeCode isEqualToString:MJPropertyTypeLongLong.uppercaseString]) {
27+
return @(self.unsignedLongLongValue);
28+
} else if ([typeCode isEqualToString:MJPropertyTypeLong]) {
29+
return @(self.longValue);
30+
} else if ([typeCode isEqualToString:MJPropertyTypeLong.uppercaseString]) {
31+
return @(self.unsignedLongValue);
32+
} else {
33+
return @(self.doubleValue);
34+
}
35+
}
36+
37+
@end
38+
1939
@implementation NSObject (MJKeyValue)
2040

2141
#pragma mark - 错误
@@ -138,54 +158,53 @@ - (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)
138158
} else { // 字典数组-->模型数组
139159
value = [objectClass mj_objectArrayWithKeyValuesArray:value context:context];
140160
}
141-
} else {
142-
if (propertyClass == [NSString class]) {
143-
if ([value isKindOfClass:[NSNumber class]]) {
144-
// NSNumber -> NSString
145-
value = [value description];
146-
} else if ([value isKindOfClass:[NSURL class]]) {
147-
// NSURL -> NSString
148-
value = [value absoluteString];
161+
} else if (propertyClass == [NSString class]) {
162+
if ([value isKindOfClass:[NSNumber class]]) {
163+
// NSNumber -> NSString
164+
value = [value description];
165+
} else if ([value isKindOfClass:[NSURL class]]) {
166+
// NSURL -> NSString
167+
value = [value absoluteString];
168+
}
169+
} else if ([value isKindOfClass:[NSString class]]) {
170+
if (propertyClass == [NSURL class]) {
171+
// NSString -> NSURL
172+
// 字符串转码
173+
value = [value mj_url];
174+
} else if (type.isNumberType) {
175+
NSString *oldValue = value;
176+
177+
// NSString -> NSDecimalNumber, 使用 DecimalNumber 来转换数字, 避免丢失精度以及溢出
178+
NSDecimalNumber *decimalValue = [NSDecimalNumber decimalNumberWithString:oldValue locale:nil];
179+
180+
// 检查特殊情况
181+
if (decimalValue == NSDecimalNumber.notANumber) {
182+
value = @(0);
183+
}else if (propertyClass != [NSDecimalNumber class]) {
184+
value = [decimalValue standardValueWithTypeCode:type.code];
185+
} else {
186+
value = decimalValue;
149187
}
150-
} else if ([value isKindOfClass:[NSString class]]) {
151-
if (propertyClass == [NSURL class]) {
152-
// NSString -> NSURL
153-
// 字符串转码
154-
value = [value mj_url];
155-
} else if (type.isNumberType) {
156-
NSString *oldValue = value;
157-
158-
// NSString -> NSNumber
159-
if (type.typeClass == [NSDecimalNumber class]) {
160-
value = [NSDecimalNumber decimalNumberWithString:oldValue];
161-
} else {
162-
NSDecimalNumber *decimalValue = [NSDecimalNumber decimalNumberWithString:oldValue];
163-
value = decimalValue == [NSDecimalNumber notANumber] ? @(0) : @(decimalValue.doubleValue);
164-
}
165-
166-
// 如果是BOOL
167-
if (type.isBoolType) {
168-
// 字符串转BOOL(字符串没有charValue方法)
169-
// 系统会调用字符串的charValue转为BOOL类型
170-
NSString *lower = [oldValue lowercaseString];
171-
if ([lower isEqualToString:@"yes"] || [lower isEqualToString:@"true"]) {
172-
value = @YES;
173-
} else if ([lower isEqualToString:@"no"] || [lower isEqualToString:@"false"]) {
174-
value = @NO;
175-
}
188+
189+
// 如果是BOOL
190+
if (type.isBoolType) {
191+
// 字符串转BOOL(字符串没有charValue方法)
192+
// 系统会调用字符串的charValue转为BOOL类型
193+
NSString *lower = [oldValue lowercaseString];
194+
if ([lower isEqualToString:@"yes"] || [lower isEqualToString:@"true"]) {
195+
value = @YES;
196+
} else if ([lower isEqualToString:@"no"] || [lower isEqualToString:@"false"]) {
197+
value = @NO;
176198
}
177199
}
178-
} else if ([value isKindOfClass:[NSNumber class]] && propertyClass == [NSDecimalNumber class]){
179-
// 过滤 NSDecimalNumber类型
180-
if (![value isKindOfClass:[NSDecimalNumber class]]) {
181-
value = [NSDecimalNumber decimalNumberWithDecimal:[((NSNumber *)value) decimalValue]];
182-
}
183200
}
184-
185-
// value和property类型不匹配
186-
if (propertyClass && ![value isKindOfClass:propertyClass]) {
187-
value = nil;
201+
} else if ([value isKindOfClass:[NSNumber class]] && propertyClass == [NSDecimalNumber class]){
202+
// 过滤 NSDecimalNumber类型
203+
if (![value isKindOfClass:[NSDecimalNumber class]]) {
204+
value = [NSDecimalNumber decimalNumberWithDecimal:[((NSNumber *)value) decimalValue]];
188205
}
206+
} else if (propertyClass && ![value isKindOfClass:propertyClass]) { // value和property类型不匹配
207+
value = nil;
189208
}
190209

191210
// 3.赋值
Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,36 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
3+
<device id="retina6_1" orientation="portrait" appearance="light"/>
34
<dependencies>
4-
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
5+
<deployment identifier="iOS"/>
6+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
57
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
68
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
79
</dependencies>
810
<scenes>
911
<!--View Controller-->
1012
<scene sceneID="tne-QT-ifu">
1113
<objects>
12-
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
14+
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
1315
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
14-
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
16+
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
1517
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
18+
<subviews>
19+
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Find examples in UnitTest files." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Trl-sJ-gBw">
20+
<rect key="frame" x="36" y="368" width="341" height="160"/>
21+
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
22+
<fontDescription key="fontDescription" type="system" pointSize="20"/>
23+
<nil key="textColor"/>
24+
<nil key="highlightedColor"/>
25+
</label>
26+
</subviews>
1627
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
1728
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
1829
</view>
1930
</viewController>
2031
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
2132
</objects>
33+
<point key="canvasLocation" x="137.68115942028987" y="137.94642857142856"/>
2234
</scene>
2335
</scenes>
2436
</document>

MJExtensionTests/MJExtensionTests.m

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,19 @@ - (void)testJSON2Model {
3030
NSDictionary *dict = @{
3131
@"name" : @"Jack",
3232
@"icon" : @"lufy.png",
33-
@"age" : @"20",
33+
@"age" : @"2147483647",
34+
@"age2": @"4294967295",
3435
@"height" : @1.55,
35-
@"money" : @"100.9",
36+
@"money" : @"100.7777777",
3637
@"sex" : @(SexFemale),
3738
@"gay" : @"1",
3839
@"speed" : @"120.5",
39-
@"identifier" : @"3443623624362",
40+
@"identifier" : @"9223372036854775807",
41+
@"identifier2" : @"18446744073709551615",
4042
@"price" : @"20.3",
4143
@"rich" : @"2",
42-
@"collect" : @"40个"
44+
@"collect" : @"40个",
45+
@"alien": @"yr Joking"
4346
};
4447

4548
// 2.将字典转为MJUser模型
@@ -48,16 +51,19 @@ - (void)testJSON2Model {
4851
// 3.检测
4952
XCTAssert([user.name isEqual:@"Jack"]);
5053
XCTAssert([user.icon isEqual:@"lufy.png"]);
51-
XCTAssert(user.age == 20);
52-
XCTAssert(user.height.doubleValue == 1.55);
53-
XCTAssert(user.money.doubleValue == 100.9);
54+
XCTAssert(user.age == INT_MAX);
55+
XCTAssert(user.age2 == UINT_MAX);
56+
XCTAssert([user.height isEqualToNumber:@(1.55)]);
57+
XCTAssert([user.money compare:[NSDecimalNumber decimalNumberWithString:@"100.7777777"]] == NSOrderedSame);
5458
XCTAssert(user.sex == SexFemale);
55-
XCTAssert(user.gay == YES);
59+
XCTAssert(user.gay);
5660
XCTAssert(user.speed == 120);
57-
XCTAssert(user.identifier == 3443623624362);
61+
XCTAssert(user.identifier == LONG_LONG_MAX);
62+
XCTAssert(user.identifier2 == ULONG_LONG_MAX);
5863
XCTAssert(user.price == 20.3);
59-
XCTAssert(user.rich == YES);
64+
XCTAssert(user.rich);
6065
XCTAssert(user.collect == 40);
66+
XCTAssert(!user.alien);
6167
}
6268

6369
- (void)testJSON2NumberModel {

MJExtensionTests/Model/MJUser.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ typedef enum {
1919
/** 头像 */
2020
@property (copy, nonatomic) NSString *icon;
2121
/** 年龄 */
22-
@property (assign, nonatomic) unsigned int age;
22+
@property (assign, nonatomic) int age;
23+
@property (assign, nonatomic) unsigned int age2;
2324
/** 身高 */
2425
@property (strong, nonatomic) NSNumber *height;
2526
/** 财富 */
@@ -32,6 +33,7 @@ typedef enum {
3233
@property (assign, nonatomic) NSInteger speed;
3334
/** 标识 */
3435
@property (assign, nonatomic) long long identifier;
36+
@property (assign, nonatomic) unsigned long long identifier2;
3537
/** 价格 */
3638
@property (assign, nonatomic) double price;
3739
/** 赞 */
@@ -41,4 +43,7 @@ typedef enum {
4143
/** 富有 */
4244
@property (assign, nonatomic) BOOL rich;
4345

46+
/** 一定为 NO, 用来测试无效数据 @"alien": @"yr Joking" */
47+
@property (assign, nonatomic) BOOL alien;
48+
4449
@end

0 commit comments

Comments
 (0)