2009-08-07 10 views
4

列挙型をXMLに格納してそれを再び読み込む最もクリーンな方法は何ですか?Cの列挙型と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]]]; 

...しかし、それは完全に理想的ではありません。私はenumを変更するたびに両方のリストを更新することに満足していません。しかしそれは受け入れられる。多くの場合、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の関数を気にしません。それが唯一の方法なら、私はプリプロセッサのものを使うことさえあります。ここで

答えて

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

ヘッダファイルに列挙型と文字列の両方を保持するための素晴らしいアイデアです。 NSArrayメソッドにすることについて何か気になるものがありますが、とにかくそれをやると思います。ありがとう! – andyvn22

+0

check関数はどこに定義されていますか? – zekel

+1

checkはAssertMacros.hでverify、require、および_noerr、_action、_quiet、_stringの各バリエーションとともに定義されています。コード全体に頻繁に振りかける必要がありますので、ユーザーが行う前にバグを見つけることができます。 –

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つの場所、元列挙し、その文字列名に整数値をマッピングし、配列にあなたの列挙値を入れて持ってしまいます。実際にマッピングを行う2つの関数はマップのコピーを持っていません。

0

XMLの大きな点は、コード化してもほとんど何にでも変換できることです。一度翻訳をするだけで(多くの)努力が必要です。 XMLをコードに変換したいくつかのプロジェクトで働いています。そして、これは多くの時間を節約しました。このテクニックは、例えば、 "XSLT Cookbook 2nd edition、S。Mangano、O'Reilley"という本の第12章で取り上げられています。

それは簡単な解決策ではないですが、あなたは 良いマッピングを持っている場合 - 定義のシングルポイント(あなたのxml) を持っている - 列挙 と.hファイルを生成することができますが - 読み取り/書き込みにテーブルや関数を生成することができますxmlの値

列挙型の数とそれが価値がある場合に変更する頻度によって異なります。 幸運を祈る!

5

私はジョンのソリューションをエコーし​​ますが、まったく自分の繰り返しを避けるために恐ろしい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; 
} 
+0

これは本当に嫌です。 :Dありがとう! (私はNSArray戦略のカテゴリを使用するつもりですが、これは実際に遊ぶのが楽しいです) – andyvn22

+1

「本当にうっすら」?私は慣習的ではないエレガントです。 –

+0

@JonHessあなたは年を取っています、それは賛辞でした – Madbreaks

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