Skip to content

Commit

Permalink
fix a problem that conversion from a large number like "1320099695068…
Browse files Browse the repository at this point in the history
…62891" 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
  • Loading branch information
wolfcon authored Sep 25, 2019
1 parent 88f2519 commit d0b475b
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 58 deletions.
105 changes: 62 additions & 43 deletions MJExtension/NSObject+MJKeyValue.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,26 @@
#import "NSString+MJExtension.h"
#import "NSObject+MJClass.h"

@implementation NSDecimalNumber(MJKeyValue)

- (id)standardValueWithTypeCode:(NSString *)typeCode {
// 由于这里涉及到编译器问题, 暂时保留 Long, 实际上在 64 位系统上, 这 2 个精度范围相同,
// 32 位略有不同, 其余都可使用 Double 进行强转不丢失精度
if ([typeCode isEqualToString:MJPropertyTypeLongLong]) {
return @(self.longLongValue);
} else if ([typeCode isEqualToString:MJPropertyTypeLongLong.uppercaseString]) {
return @(self.unsignedLongLongValue);
} else if ([typeCode isEqualToString:MJPropertyTypeLong]) {
return @(self.longValue);
} else if ([typeCode isEqualToString:MJPropertyTypeLong.uppercaseString]) {
return @(self.unsignedLongValue);
} else {
return @(self.doubleValue);
}
}

@end

@implementation NSObject (MJKeyValue)

#pragma mark - 错误
Expand Down Expand Up @@ -138,54 +158,53 @@ - (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)
} else { // 字典数组-->模型数组
value = [objectClass mj_objectArrayWithKeyValuesArray:value context:context];
}
} else {
if (propertyClass == [NSString class]) {
if ([value isKindOfClass:[NSNumber class]]) {
// NSNumber -> NSString
value = [value description];
} else if ([value isKindOfClass:[NSURL class]]) {
// NSURL -> NSString
value = [value absoluteString];
} else if (propertyClass == [NSString class]) {
if ([value isKindOfClass:[NSNumber class]]) {
// NSNumber -> NSString
value = [value description];
} else if ([value isKindOfClass:[NSURL class]]) {
// NSURL -> NSString
value = [value absoluteString];
}
} else if ([value isKindOfClass:[NSString class]]) {
if (propertyClass == [NSURL class]) {
// NSString -> NSURL
// 字符串转码
value = [value mj_url];
} else if (type.isNumberType) {
NSString *oldValue = value;

// NSString -> NSDecimalNumber, 使用 DecimalNumber 来转换数字, 避免丢失精度以及溢出
NSDecimalNumber *decimalValue = [NSDecimalNumber decimalNumberWithString:oldValue locale:nil];

// 检查特殊情况
if (decimalValue == NSDecimalNumber.notANumber) {
value = @(0);
}else if (propertyClass != [NSDecimalNumber class]) {
value = [decimalValue standardValueWithTypeCode:type.code];
} else {
value = decimalValue;
}
} else if ([value isKindOfClass:[NSString class]]) {
if (propertyClass == [NSURL class]) {
// NSString -> NSURL
// 字符串转码
value = [value mj_url];
} else if (type.isNumberType) {
NSString *oldValue = value;

// NSString -> NSNumber
if (type.typeClass == [NSDecimalNumber class]) {
value = [NSDecimalNumber decimalNumberWithString:oldValue];
} else {
NSDecimalNumber *decimalValue = [NSDecimalNumber decimalNumberWithString:oldValue];
value = decimalValue == [NSDecimalNumber notANumber] ? @(0) : @(decimalValue.doubleValue);
}

// 如果是BOOL
if (type.isBoolType) {
// 字符串转BOOL(字符串没有charValue方法)
// 系统会调用字符串的charValue转为BOOL类型
NSString *lower = [oldValue lowercaseString];
if ([lower isEqualToString:@"yes"] || [lower isEqualToString:@"true"]) {
value = @YES;
} else if ([lower isEqualToString:@"no"] || [lower isEqualToString:@"false"]) {
value = @NO;
}

// 如果是BOOL
if (type.isBoolType) {
// 字符串转BOOL(字符串没有charValue方法)
// 系统会调用字符串的charValue转为BOOL类型
NSString *lower = [oldValue lowercaseString];
if ([lower isEqualToString:@"yes"] || [lower isEqualToString:@"true"]) {
value = @YES;
} else if ([lower isEqualToString:@"no"] || [lower isEqualToString:@"false"]) {
value = @NO;
}
}
} else if ([value isKindOfClass:[NSNumber class]] && propertyClass == [NSDecimalNumber class]){
// 过滤 NSDecimalNumber类型
if (![value isKindOfClass:[NSDecimalNumber class]]) {
value = [NSDecimalNumber decimalNumberWithDecimal:[((NSNumber *)value) decimalValue]];
}
}
// value和property类型不匹配
if (propertyClass && ![value isKindOfClass:propertyClass]) {
value = nil;
} else if ([value isKindOfClass:[NSNumber class]] && propertyClass == [NSDecimalNumber class]){
// 过滤 NSDecimalNumber类型
if (![value isKindOfClass:[NSDecimalNumber class]]) {
value = [NSDecimalNumber decimalNumberWithDecimal:[((NSNumber *)value) decimalValue]];
}
} else if (propertyClass && ![value isKindOfClass:propertyClass]) { // value和property类型不匹配
value = nil;
}

// 3.赋值
Expand Down
20 changes: 16 additions & 4 deletions MJExtensionDemo/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,24 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<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">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<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">
<rect key="frame" x="36" y="368" width="341" height="160"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="20"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="137.68115942028987" y="137.94642857142856"/>
</scene>
</scenes>
</document>
26 changes: 16 additions & 10 deletions MJExtensionTests/MJExtensionTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,19 @@ - (void)testJSON2Model {
NSDictionary *dict = @{
@"name" : @"Jack",
@"icon" : @"lufy.png",
@"age" : @"20",
@"age" : @"2147483647",
@"age2": @"4294967295",
@"height" : @1.55,
@"money" : @"100.9",
@"money" : @"100.7777777",
@"sex" : @(SexFemale),
@"gay" : @"1",
@"speed" : @"120.5",
@"identifier" : @"3443623624362",
@"identifier" : @"9223372036854775807",
@"identifier2" : @"18446744073709551615",
@"price" : @"20.3",
@"rich" : @"2",
@"collect" : @"40个"
@"collect" : @"40个",
@"alien": @"yr Joking"
};

// 2.将字典转为MJUser模型
Expand All @@ -48,16 +51,19 @@ - (void)testJSON2Model {
// 3.检测
XCTAssert([user.name isEqual:@"Jack"]);
XCTAssert([user.icon isEqual:@"lufy.png"]);
XCTAssert(user.age == 20);
XCTAssert(user.height.doubleValue == 1.55);
XCTAssert(user.money.doubleValue == 100.9);
XCTAssert(user.age == INT_MAX);
XCTAssert(user.age2 == UINT_MAX);
XCTAssert([user.height isEqualToNumber:@(1.55)]);
XCTAssert([user.money compare:[NSDecimalNumber decimalNumberWithString:@"100.7777777"]] == NSOrderedSame);
XCTAssert(user.sex == SexFemale);
XCTAssert(user.gay == YES);
XCTAssert(user.gay);
XCTAssert(user.speed == 120);
XCTAssert(user.identifier == 3443623624362);
XCTAssert(user.identifier == LONG_LONG_MAX);
XCTAssert(user.identifier2 == ULONG_LONG_MAX);
XCTAssert(user.price == 20.3);
XCTAssert(user.rich == YES);
XCTAssert(user.rich);
XCTAssert(user.collect == 40);
XCTAssert(!user.alien);
}

- (void)testJSON2NumberModel {
Expand Down
7 changes: 6 additions & 1 deletion MJExtensionTests/Model/MJUser.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ typedef enum {
/** 头像 */
@property (copy, nonatomic) NSString *icon;
/** 年龄 */
@property (assign, nonatomic) unsigned int age;
@property (assign, nonatomic) int age;
@property (assign, nonatomic) unsigned int age2;
/** 身高 */
@property (strong, nonatomic) NSNumber *height;
/** 财富 */
Expand All @@ -32,6 +33,7 @@ typedef enum {
@property (assign, nonatomic) NSInteger speed;
/** 标识 */
@property (assign, nonatomic) long long identifier;
@property (assign, nonatomic) unsigned long long identifier2;
/** 价格 */
@property (assign, nonatomic) double price;
/** 赞 */
Expand All @@ -41,4 +43,7 @@ typedef enum {
/** 富有 */
@property (assign, nonatomic) BOOL rich;

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

@end

3 comments on commit d0b475b

@vchao
Copy link

@vchao vchao commented on d0b475b Sep 26, 2019

Choose a reason for hiding this comment

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

快点releases吧,更新到3.1.2后long long字典转模型全都出现问题了

@boai
Copy link

@boai boai commented on d0b475b Oct 8, 2019

Choose a reason for hiding this comment

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

这个版本为啥把
else if (propertyClass && ![value isKindOfClass:propertyClass]) { // value和property类型不匹配 value = nil; } }
给加了个else ,之前的老代码好多类型不对的。。。

@wolfcon
Copy link
Collaborator Author

@wolfcon wolfcon commented on d0b475b Oct 8, 2019

Choose a reason for hiding this comment

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

已经修复 #742, 感谢反馈

Please sign in to comment.