簡體   English   中英

在C enum和XML之間轉換

[英]Converting between C enum and XML

在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函數。 我甚至會使用預處理器的東西,如果這是唯一的方法。

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

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) - 可以用enum生成.h文件 - 可以生成表或函數來讀取/寫入xml中的值

這取決於枚舉的數量以及它們在有價值時的變化頻率。 祝好運!

我正在嘗試這個解決方案 -

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM