C 枚举和 XML 之间的转换

发布于 2024-08-01 18:45:42 字数 1202 浏览 6 评论 0原文

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

What's the cleanest way to store an enum in XML and read it back out again? Say I've got:

enum ETObjectType {ETNormalObjectType, ETRareObjectType, ETEssentialObjectType};

...and I want to take a variable, enum ETObjectType objectType = ETNormalObjectType;, and convert it to XML that looks like this: <objectType>ETNormalObjectType</objectType>.

Currently what I'm doing is something like this:

NSString* const ETObjectTypeAsString[] = {@"ETNormalObjectType",@"ETRareObjectType",@"ETEssentialObjectType"};

[anXMLElement addChild:[NSXMLElement elementWithName:@"objectType" stringValue:ETObjectTypeAsString[objectType]]];

...but that's not entirely ideal; I'm not happy about updating both lists every time I change my enum. But it's acceptable. Much, much worse is reading XML back in, for which I am currently doing this:

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

Yuck! This disgusts me. There's got to be a cleaner way to read, at least, or perhaps a unified way to read and write?

I'm using Obj-C and Cocoa, but I wouldn't mind some pure C functions. I'd even use preprocessor stuff, if it's the only way.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

勿忘初心 2024-08-08 18:45:42

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

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

I'm experimenting with this solution -

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

I like it because it checks the length of the string array at compile time, and the enumFromStrings function is generic and reusable. You call it like so:

-(void)setType:(NSString*)typeString{
    type = enumFromStrings(typeString,stepTypeEnum,kStepTypeCount);
}
我为君王 2024-08-08 18:45:42

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

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

I haven't found a better way than duplicating the enum in a string. However, I do it slightly differently, namely:

typedef enum {
    kManipulateWindowTargetFrontWindow,
    kManipulateWindowTargetNamedWindow, 
    kManipulateWindowTargetWindowNameContaining, 
    kManipulateWindowTargetDEFAULT = kManipulateWindowTargetFrontWindow, 
} ManipulateWindowTargetType;
#define kManipulateWindowTargetTypeNamesArray @"FrontWindow", @"NamedWindow", @"WindowNameContaining", nil

then in the implementation:

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

The reason I use the #define is to avoid declaring the array in the header file, but it would be insane to separate the definition of the enum from the definition of the sequence of strings, so this is the best compromise I've found.

Since the code is boilerplate, you can actually make them a category on 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

and then:

NSLog( @"s is %@", [kManipulateWindowTargetTypeArray stringWithEnum:kManipulateWindowTargetNamedWindow] );
ManipulateWindowTargetType mwtt = (ManipulateWindowTargetType)[kManipulateWindowTargetTypeArray enumFromString:@"WindowNameContaining" default:kManipulateWindowTargetDEFAULT];
NSLog( @"e is %d", mwtt );
冬天旳寂寞 2024-08-08 18:45:42

我赞同乔恩的解决方案,但您可以使用可怕的 X-macro 来避免重复你自己。 我不知道如何用代码格式评论乔恩的答案,所以这里将其作为一个新答案。

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

I echo Jon's solution, but you can use the dreaded X-macro to avoid repeating yourself at all. I don't know how to comment on Jon's answer with code formatting, so here it is as a new answer.

#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;
}
沉默的熊 2024-08-08 18:45:42

XML 的伟大之处在于它几乎可以转换为任何内容,甚至可以转换为代码。 翻译一次只需要(很多)努力。 我参与过多个将 XML 转换为代码的项目。 这节省了很多时间。 例如,“XSLT Cookbook 第 2 版,S. Mangano,O'Reilley”一书的第 12 章介绍了该技术。

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

这取决于枚举的数量以及它们更改的频率(如果值得)。
祝你好运!

The great thing of XML is that it can transform to nearly anything, even to code. It just takes (a lot of) effort to make the translation once. I have worked in several projects where XML was translated to code. And this saved a lot of time. This technique is for instance, covered in chapter 12 of the book "XSLT Cookbook 2nd edition, S. Mangano, O'Reilley".

It's not an easy solution, but if you have a good mapping you
- have a single point of definition (your xml)
- can generate .h files with enum
- can generate tables or functions to read/write values in xml

It depends on the number of enums and how often they change if it is worthwhile.
Good luck!

小猫一只 2024-08-08 18:45:42

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

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

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

Here's how I typically write these styles of methods:

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

You end up having to put your enumeration values in two places, the original enumeration, and the array that maps integer values to their string names. The two functions that actually do the mapping don't have copies of the maps though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文