在C enum和XML之间转换

时间:2009-08-07 04:42:23

标签: c objective-c xml cocoa enums

在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函数。我甚至会使用预处理器,如果这是唯一的方法。

5 个答案:

答案 0 :(得分:17)

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

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 );

答案 1 :(得分:5)

以下是我通常编写这些方法风格的方法:

#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;
}

您最终必须将枚举值放在两个位置,即原始枚举,以及将整数值映射到其字符串名称的数组。实际进行映射的两个函数虽然没有地图的副本。

答案 2 :(得分:5)

我回应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;
}

答案 3 :(得分:0)

XML的优点在于它可以转换为几乎任何东西,甚至是代码。只需(很多)努力就可以进行一次翻译。我曾在几个项目中工作,其中XML被转换为代码。这节省了很多时间。例如,该技术在“XSLT Cookbook 2nd edition,S。Mangano,O'Reilley”一书的第12章中有所介绍。

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

这取决于枚举的数量以及它们在有价值时的变化频率。 祝你好运!

答案 4 :(得分:0)

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

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);
}