在C enum和XML之间转换

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

enum ETObjectType {ETNormalObjectType, ETRareObjectType, ETEssentialObjectType}; 

…我想要一个变量, enum ETObjectType objectType = ETNormalObjectType; ,并将其转换为如下所示的XML: ETNormalObjectType

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

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

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

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

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

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

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

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

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

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

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

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

 static NSString *stepTypeEnum[kStepTypeCount] = {@"one",@"two",@"three",@"four"}; int enumFromStrings(NSString*findString,NSString *strings[],int enumMax){ for (int i=0;i 

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

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