quicktype icon indicating copy to clipboard operation
quicktype copied to clipboard

(cJSON) Enum Conversion Will Default to First Enum When Invalid or null data is provided

Open pseudotronics opened this issue 2 years ago • 0 comments

I have run into an issue where the cJSON implementation of enums produces what to me is an unacceptable result.

Take for instance the following enum:

        "subscription": {
            "type": "string",
            "enum": [
                "state",
                "config",
                "heartbeat"
            ]
        }

The current cJSON implementation produces this result:

enum Subscription {
    SUBSCRIPTION_CONFIG,
    SUBSCRIPTION_HEARTBEAT,
    SUBSCRIPTION_STATE
};

enum Subscription cJSON_GetSubscriptionValue(const cJSON * j) {
    enum Subscription x = 0;
    if (NULL != j) {
        if (!strcmp(cJSON_GetStringValue(j), "config")) x = SUBSCRIPTION_CONFIG;
        else if (!strcmp(cJSON_GetStringValue(j), "heartbeat")) x = SUBSCRIPTION_HEARTBEAT;
        else if (!strcmp(cJSON_GetStringValue(j), "state")) x = SUBSCRIPTION_STATE;
    }
    return x;
}

The problem is that 0 is SUBSCRIPTION_CONFIG. That means when the subscription is null or something undefined the value returned is always SUBSCRIPTION_CONFIG.

The easiest way to correct this would be to always set the first enum = 1 when declaring the enum.

enum Subscription {
    SUBSCRIPTION_CONFIG = 1,
    SUBSCRIPTION_HEARTBEAT,
    SUBSCRIPTION_STATE
};

I have this implemented as a styling option in my fork, but I am unsure if this is the best approach. I don't fully understand the base enum definition in quicktype. It looks like there is a provision for assigning values baked in, but I could not figure out how to make that happen.

Here is the relevant emit block that appears to have provision for emitting the enum with a value. I added the first ? " = 1" : "" but like i said before I am unsure if this is a good approach.

        this.emitBlock(
            ["enum ", enumName],
            () => {
                const combinedName = allUpperWordStyle(this.sourcelikeToString(enumName));
                this.forEachEnumCase(enumType, "none", (name, jsonName) => {
                    if (enumValues !== undefined) {
                        const [enumValue] = getAccessorName(enumValues, jsonName);
                        if (enumValue !== undefined) {
                            this.emitLine(combinedName, "_", name, " = ", enumValue.toString(), ",");
                        } else {
                            this.emitLine(combinedName, "_", name, first ? " = 1" : "", ",");
                        }
                    } else {
                        this.emitLine(combinedName, "_", name, first ? " = 1" : "", ",");
                    }
                    first = false;
                });
            },
            "",
            true
        );

pseudotronics avatar Aug 04 '23 12:08 pseudotronics