解析和更改 NSPredicate

Posted

技术标签:

【中文标题】解析和更改 NSPredicate【英文标题】:Parsing and changing NSPredicate 【发布时间】:2013-10-18 14:18:36 【问题描述】:

我必须将数据从以前的应用版本迁移到新版本。这也会影响用户保存的一些谓词(NSPredicate 实例),这意味着我必须以编程方式更改它们。

目前我尝试解析使用[NSPredicate predicateFormat] 获得的字符串并手动更改一些表达式。例如oldKeyPath == "something"newKeyPath == "something"。但这感觉像是一种 hack,我很好奇是否有更好的方法?

我阅读了 Apple 关于使用 NSPredicate 和 NSExpression 进行编程的文档。有很多方法可以从NSExpressions 组合NSPredicate 对象。我希望找到从NSPredicate 获取NSExpression 对象的相反方法。我错过了什么吗?

感谢您的任何提示。

解决方案

感谢 Martin R,我能够在 NSPredicate 上创建一个类别,允许我对表达式进行修改。

@implementation NSPredicate (ExtractComparisions)

- (NSPredicate *)predicateByChangingComparisionsWithBlock:(NSPredicate *(^)(NSComparisonPredicate *))block 
  if ([self isKindOfClass: [NSCompoundPredicate class]]) 
    NSCompoundPredicate *compPred = (NSCompoundPredicate *)self;
    NSMutableArray *predicates = [NSMutableArray array];
    for (NSPredicate *predicate in [compPred subpredicates]) 
      NSPredicate *newPredicate = [predicate predicateByChangingComparisionsWithBlock: block];
      if (newPredicate != nil)
        [predicates addObject: newPredicate];
    
    return [[[NSCompoundPredicate alloc] initWithType: compPred.compoundPredicateType
                                       subpredicates: predicates] autorelease];
   if ([self isKindOfClass: [NSComparisonPredicate class]]) 
    return block((NSComparisonPredicate *)self);
  
  return self;


@end

这是一个示例代码,说明如何使用它

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification

  NSPredicate *predicate = [NSPredicate predicateWithFormat: @"key.path like %@ and count > 17", @"Hello"];
  NSPredicate *newPredicate = [predicate predicateByChangingComparisionsWithBlock:
                               ^NSPredicate *(NSComparisonPredicate *cp) 
    NSExpression *left = [cp leftExpression];
    NSExpression *right = [cp rightExpression];
    if ([[cp leftExpression] expressionType] == NSKeyPathExpressionType) 
      NSString *keyPath = [[cp leftExpression] keyPath];
      if ([keyPath isEqualToString: @"key.path"])
        left = [NSExpression expressionForKeyPath: @"key.new.path"];
      return [NSComparisonPredicate predicateWithLeftExpression: left
                                                rightExpression: right
                                                       modifier: cp.comparisonPredicateModifier
                                                           type: cp.predicateOperatorType
                                                        options:cp.options];
    
    return cp;
  ];
  NSLog(@"Before: %@", predicate);
  NSLog(@"After: %@", newPredicate);

【问题讨论】:

不错!请注意,您还可以为每个子类 NSCompoundPredicate 和 NSComparisonPredicate 实现不同的类别方法。这使得 isKindOfClass 检查和强制转换过时(并且看起来很复杂:-) 是的,我考虑过这一点,但我意识到我仍然必须调用超类 NSPredicate 上的方法,因为我不知道类的确切类型,或者我必须先转换它。所以我做出了妥协并“隐藏”了超类中的演员表。 NSPredicate 看起来不像是我们必须通过子类扩展的类。但我仍然同意更复杂的方式。也许我稍后会在我的代码中更改它。 —— 【参考方案1】:

(这只是一个想法。)每个谓词都是一个子类的实例,例如 NSCompoundPredicateNSComparisonPredicate。所以你可以检查实际 谓词的类,将其强制转换为该类的对象,然后 检查其属性。如有必要,使用子谓词或重复该过程 表达式。

以下只是一个简单复合谓词的示例 以这种方式“解剖”。它不是检查所有情况的通用解决方案, 但可能会为您指明正确的方向。

NSPredicate *p0 = [NSPredicate predicateWithFormat:@"foo = 'bar' AND count > 17"];

if ([p0 isKindOfClass:[NSCompoundPredicate class]]) 
    NSCompoundPredicate *p0a = (NSCompoundPredicate *)p0;
    NSCompoundPredicateType type0 = p0a.compoundPredicateType;  // NSAndPredicateType
    NSPredicate *p1 = p0a.subpredicates[0];                     // foo = 'bar'
    NSPredicate *p2 = p0a.subpredicates[1];                     // count > 17
    if ([p1 isKindOfClass:[NSComparisonPredicate class]]) 
        NSComparisonPredicate *p1a = (NSComparisonPredicate *)p1;
        NSPredicateOperatorType type1 = p1a.predicateOperatorType;  // NSEqualToPredicateOperatorType
        NSExpression *e3 = p1a.leftExpression;                  // foo, NSKeyPathExpression
        NSExpression *e4 = p1a.rightExpression;                 // "bar", NSConstantValueExpression
    
    if ([p2 isKindOfClass:[NSComparisonPredicate class]]) 
        NSComparisonPredicate *p2a = (NSComparisonPredicate *)p2;
        NSPredicateOperatorType type2 = p2a.predicateOperatorType;  // NSGreaterThanPredicateOperatorType
        NSExpression *e5 = p2a.leftExpression;                  // count, NSKeyPathExpression
        NSExpression *e6 = p2a.rightExpression;                 // 17, NSConstantValueExpression
    

【讨论】:

感谢您指出正确的方向!这很棒,也很简单。我将更新我的问题以添加受您的回答影响的解决方案。

以上是关于解析和更改 NSPredicate的主要内容,如果未能解决你的问题,请参考以下文章

Joda-Time、夏令时更改和日期时间解析

如何在 Rails 中解析 CSV 期间更改编码

Rails 4.2 强参数和解析 json put/update 上的更改属性

在 DST 更改发生时将字符串解析为日期

java SAX解析器在工作代码更改后给出NullPointerException

解析并对其进行一些更改后,为多部分电子邮件设置的内容类型应该是啥?