Преобразование между C enum и XML - PullRequest
4 голосов
/ 07 августа 2009

Какой самый простой способ сохранить перечисление в 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 и Какао, но я не возражаю против некоторых чистых функций Си. Я бы даже использовал препроцессор, если только так.

Ответы [ 5 ]

17 голосов
/ 07 августа 2009

Я не нашел лучшего способа, чем дублировать перечисление в строке. Однако я делаю это немного по-другому, а именно:

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, состоит в том, чтобы не объявлять массив в заголовочном файле, но было бы безумно отделять определение enum от определения последовательности строк, так что это лучший компромисс, который я нашел.

Поскольку этот код является стандартным, вы можете сделать его категорией в 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 );
5 голосов
/ 10 августа 2009

Я повторяю решение Джона, но вы можете использовать страшный X-макрос , чтобы вообще не повторяться. Я не знаю, как прокомментировать ответ Джона с форматированием кода, так что здесь это как новый ответ.

#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;
}
5 голосов
/ 07 августа 2009

Вот как я обычно пишу эти стили методов:

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

В итоге вам придется поместить значения перечисления в два места: исходное перечисление и массив, который отображает целочисленные значения в их строковые имена. Хотя две функции, которые на самом деле выполняют сопоставление, не имеют копий карт.

0 голосов
/ 27 августа 2010

Я экспериментирую с этим решением -

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);
}
0 голосов
/ 07 августа 2009

Отличительной особенностью XML является то, что он может преобразовываться практически во что угодно, даже в код. Требуется (много) усилий, чтобы сделать перевод один раз. Я работал в нескольких проектах, где XML был переведен в код. И это сэкономило много времени. Например, эта техника описана в главе 12 книги «Поваренная книга XSLT, 2-е издание, С. Мангано, О'Рейли».

Это не простое решение, но если у вас есть хорошее отображение, вы - иметь единую точку определения (ваш XML) - может генерировать .h файлы с enum - может генерировать таблицы или функции для чтения / записи значений в xml

Это зависит от количества перечислений и от того, как часто они меняются, если оно того стоит. Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...