【问题标题】:Converting between C enum and XMLC enum 和 XML 之间的转换
【发布时间】:2009-08-07 04:42:23
【问题描述】:

在 XML 中存储枚举并再次读取它的最简洁的方法是什么?说我有:

enum ETObjectType {ETNormalObjectType, ETRareObjectType, ETEssentialObjectType};

...我想获取一个变量 enum ETObjectType objectType = ETNormalObjectType;,并将其转换为如下所示的 XML:<objectType>ETNormalObjectType</objectType>

目前我正在做的事情是这样的:

NSString* const ETObjectTypeAsString[] = {@"ETNormalObjectType",@"ETRareObjectType",@"ETEssentialObjectType"};

[anXMLElement addChild:[NSXMLElement elementWithName:@"objectType" stringValue:ETObjectTypeAsString[objectType]]];

...但这并不完全理想;每次更改枚举时,我都对更新这两个列表不满意。但这是可以接受的。更糟糕的是读回 XML,我目前正在这样做:

if ([[[anXMLElement childNamed:@"objectType"] stringValue] isEqualToString:@"ETRareObjectType"])
{
    [self initObjectType:ETRareObjectType];
}
else if ([[[anXMLElement childNamed:@"objectType"] stringValue] isEqualToString:@"ETEssentialObjectType"])
{
    [self initObjectType:ETEssentialObjectType];
}
else
{
    [self initObjectType:ETNormalObjectType];
}

呸!这让我感到厌恶。至少必须有一种更简洁的阅读方式,或者也许是一种统一的读写方式?

我正在使用 Obj-C 和 Cocoa,但我不介意一些纯 C 函数。如果这是唯一的方法,我什至会使用预处理器。

【问题讨论】:

    标签: c objective-c xml cocoa enums


    【解决方案1】:

    我没有找到比在字符串中复制枚举更好的方法。不过,我的做法略有不同,即:

    typedef enum {
        kManipulateWindowTargetFrontWindow,
        kManipulateWindowTargetNamedWindow, 
        kManipulateWindowTargetWindowNameContaining, 
        kManipulateWindowTargetDEFAULT = kManipulateWindowTargetFrontWindow, 
    } ManipulateWindowTargetType;
    #define kManipulateWindowTargetTypeNamesArray @"FrontWindow", @"NamedWindow", @"WindowNameContaining", nil
    

    然后在实现中:

    static NSArray* kManipulateWindowTargetTypeArray = [[NSArray alloc] initWithObjects: kManipulateWindowTargetTypeNamesArray];
    
    NSString* ManipulateWindowTargetTypeToString( ManipulateWindowTargetType mwtt )
    {
        return [kManipulateWindowTargetTypeArray objectAtIndex:mwtt];
    }
    
    ManipulateWindowTargetType ManipulateWindowTargetTypeFromString( NSString* s )
    {
        NSUInteger n = [kManipulateWindowTargetTypeArray indexOfObject:s];
        check( n != NSNotFound );
        if ( n == NSNotFound ) {
            n = kManipulateWindowTargetDEFAULT;
        }
        return (ManipulateWindowTargetType) n;
    }
    

    我使用#define的原因是为了避免在头文件中声明数组,但是将枚举的定义与字符串序列的定义分开会很疯狂,所以这是我最好的折衷方案找到了。

    由于代码是样板文件,您实际上可以将它们设为 NSArray 上的一个类别。

    @interface NSArray (XMLExtensions)
    
    - (NSString*) stringWithEnum: (NSUInteger) e;
    - (NSUInteger) enumFromString: (NSString*) s default: (NSUInteger) def;
    - (NSUInteger) enumFromString: (NSString*) s;
    
    @end
    
    @implementation NSArray (XMLExtensions)
    
    - (NSString*) stringWithEnum: (NSUInteger) e;
    {
        return [self objectAtIndex:e];
    }
    
    - (NSUInteger) enumFromString: (NSString*) s default: (NSUInteger) def;
    {
        NSUInteger n = [self indexOfObject:s];
        check( n != NSNotFound );
        if ( n == NSNotFound ) {
            n = def;
        }
        return n;
    }
    
    - (NSUInteger) enumFromString: (NSString*) s;
    {
        return [self enumFromString:s default:0];
    }
    
    
    @end
    

    然后:

    NSLog( @"s is %@", [kManipulateWindowTargetTypeArray stringWithEnum:kManipulateWindowTargetNamedWindow] );
    ManipulateWindowTargetType mwtt = (ManipulateWindowTargetType)[kManipulateWindowTargetTypeArray enumFromString:@"WindowNameContaining" default:kManipulateWindowTargetDEFAULT];
    NSLog( @"e is %d", mwtt );
    

    【讨论】:

    • 在头文件中保留枚举和字符串序列的好主意。让那些成为 NSArray 方法让我感到困扰,但我可能还是会这样做。谢谢!
    • check函数在哪里定义的?
    • check 在 AssertMacros.h 中定义,以及 verify、require 和变体 _noerr、_action、_quiet、_string。它们应该经常散布在您的代码中,以便您在用户之前发现错误。
    • 我通常通过在枚举列表末尾添加一个 'MyEnumCount' 来处理字符串列表与枚举的分离,然后在 EnumToString 方法中断言数组等于“MyEnumCount - 1”。这样,如果有人添加了一个枚举并且没有更新数组,你会立即得到一个断言失败。编译时断言而不是运行时断言可以使其更加自我纠正。
    • 它在我的 .mm 文件中,但请注意,我总是将所有内容编译为 Objective-C++。 C 可能不允许使用函数调用进行静态初始化,在这种情况下,您需要使用初始化一次的静态局部变量创建静态访问器函数。那是一个静态函数,body static kManipulateWindowTargetTypeArray = nil; if ( ! kManipulateWindowTargetTypeArray ) kManipulateWindowTargetTypeArray = ...;返回 kManipulateWindowTargetTypeArray;或者,切换到 Objective-C++(唯一真正的缺点是 clang 还不能与 C++ 一起使用)。
    【解决方案2】:

    我通常是这样编写这些样式的方法的:

    #define countof(array) (sizeof(array)/sizeof(array[0]))
    
    enum {
        ETNormalObjectType,
        ETRareObjectType,
        ETEssentialObjectType
    };
    typedef NSInteger ETObjectType;
    
    NSString *ETObjectTypesAsStrings[] = {[ETNormalObjectType] = @"ETNormalObjectType", 
                                          [ETRareObjectType] = @"ETRareObjectType", 
                                          [ETEssentialObjectType] = @"ETEssentialObjectType"};
    
    NSString *ETStringFromObjectType(ETObjectType type) {
        return ETObjectTypesAsStrings[type];
    }
    
    ETObjectType ETObjectTypeFromString(NSString *string) {
        NSString *match = nil;
        for(NSInteger idx = 0; !match && (idx < countof(ETObjectTypesAsStrings)); idx += 1) {
            if ([string isEqualToString:ETObjectTypesAsStrings[idx]]) {
                match = ETObjectTypesAsStrings[idx];
            }
        }
        return match;
    }
    

    您最终不得不将枚举值放在两个位置,即原始枚举和将整数值映射到其字符串名称的数组。实际执行映射的两个函数虽然没有映射副本。

    【讨论】:

      【解决方案3】:

      我赞同 Jon 的解决方案,但您可以使用可怕的 X-macro 来避免重复自己。我不知道如何用代码格式来评论 Jon 的答案,所以这里是一个新的答案。

      #define ETObjectTypeEntries \
      ENTRY(ETNormalObjectType) \
      ENTRY(ETRareObjectType) \
      ENTRY(ETEssentialObjectType)
      
      typedef enum ETObjectType {
      #define ENTRY(objectType) objectType, 
          ETObjectTypeEntries
      #undef ENTRY
      } ETObjectType;
      
      NSString *ETObjectTypesAsStrings[] = {
      #define ENTRY(objectType) [objectType] = @"" # objectType, 
          ETObjectTypeEntries
      #undef ENTRY
      };
      
      #define countof(array) (sizeof(array)/sizeof(array[0]))
      
      NSString *ETStringFromObjectType(ETObjectType type) {
          return ETObjectTypesAsStrings[type];
      }
      
      NSString *ETObjectTypeFromString(NSString *string) {
          NSString *match = nil;
          for(NSInteger idx = 0; !match && (idx < countof(ETObjectTypesAsStrings)); idx += 1) {
              if ([string isEqualToString:ETObjectTypesAsStrings[idx]]) {
                  match = ETObjectTypesAsStrings[idx];
              }
          }
          return match;
      }
      

      【讨论】:

      • 这……真恶心。 :D 谢谢! (我将在 NSArray 策略上使用类别,但这玩起来真的很有趣。)
      • “真恶心”?我会说非常规优雅。
      • @JonHess 你老了,这是一种恭维
      【解决方案4】:

      XML 的伟大之处在于它几乎可以转换为任何东西,甚至可以转换为代码。翻译一次就需要(很多)努力。我曾在几个将 XML 转换为代码的项目中工作过。这节省了很多时间。例如,“XSLT Cookbook 2nd edition, S. Mangano, O'Reilley”一书的第 12 章介绍了这种技术。

      这不是一个简单的解决方案,但如果你有一个好的映射,你 - 有一个定义点(你的 xml) - 可以使用枚举生成 .h 文件 - 可以生成表格或函数来读取/写入xml中的值

      这取决于枚举的数量以及它们是否值得更改的频率。 祝你好运!

      【讨论】:

        【解决方案5】:

        我正在试验这个解决方案 -

        static NSString *stepTypeEnum[kStepTypeCount] = {@"one",@"two",@"three",@"four"};
        
        int enumFromStrings(NSString*findString,NSString *strings[],int enumMax){
            for (int i=0;i<enumMax;i++){
                if ([findString isEqual: strings[i]]) {
                    return i;
                }
            }
            NSLog(@"enum was not found for string %@", findString);
            assert(false);
            return INT_MAX;
        }
        

        我喜欢它,因为它在编译时检查字符串数组的长度,并且 enumFromStrings 函数是通用且可重用的。你这样称呼它:

        -(void)setType:(NSString*)typeString{
            type = enumFromStrings(typeString,stepTypeEnum,kStepTypeCount);
        }
        

        【讨论】:

          猜你喜欢
          • 2018-01-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-19
          • 2010-10-04
          • 2010-09-21
          • 2011-03-09
          • 1970-01-01
          相关资源
          最近更新 更多