有什么方法可以在CFC中强制执行属性类型?

时间:2015-02-19 07:24:17

标签: coldfusion casting railo cfc lucee

我在这里遇到一些奇怪的类型转换问题,而不是特定于Lucee(也在Railo中)。可能是因为我在这里错过了一些关键点......

我有一个组件:

<cfcomponent output="false">
    <cfproperty name="thisId" type="String" default="-1" />
    <cfproperty name="thatId" type="String" default="-1" />
</cfcomponent>

两个属性都清楚地键入字符串。我希望当我尝试将对象或数字设置为其中之一时,代码将返回错误。 但是,看到我已经习惯了cfml为我做类型转换,我从来没有想过这样一个事实,即在这里设置一个数字根本不是问题。事实上我的假设是我试图在这里设置的所有数字都将被转换为字符串。

但事实并非如此。 在以序列化结构的形式实现了包含这些组件的衍生物的一些REST调用之后,我提出了一些包含在整数中的东西,有些包含在字符串中。当我注意到这一点时,我抛弃了组件本身,并注意到设置一个字符串被预期作为属性的数字,键入已被覆盖为数字。

Railo / Lucee仍在验证的事实在我看来是无用的。验证严格类型并在正确类型的变量中抛出错误/传递,或者验证松散类型并转换为CFC预期的类型(如果可能)。 Railo / lucee在这里实现了松散类型验证,但仍然决定以原始类型传递变量,而不是cfc期望的每个s。

因为我现在不希望将每个数字强制转换成字符串,这里有一个简单的疏忽可以挽救我的打字吗?

(我已经在Lucee邮件列表中发布了这个,但没有任何结果,只是人们确认我已经说过/忽略了这不是预期行为的可能性。)


更新(由Adam提出): 我看到的是以下内容(在我上面描述的cfc组件中):

<!--- setting a string returns a string afterwards, as expected since the property is a type string initialy --->
<cfset componentName.setThisId('1') />
<cfset local.thisIsStillAString = componentName.getThisId() />

<!--- setting a number returns a number, which means we can no longer assume the property is a string, as it was initially set up --->
<cfset componentName.setThatId(12345) />
<cfset local.thisIsNoLongerAString = componentName.getThatId() />

在这两种情况下,我都希望: - 输入变量将严格评估为字符串,这意味着第二个示例会引发错误,看到它实际上是一个数字 - 输入变量将被松散地评估为字符串,但在传递评估时将被强制转换为字符串,这意味着第二个示例将通过,但最终将返回一个字符串,而不是一个数字。

在任何情况下,我都希望保留属性的原始输入,而不是将其更改为您尝试设置的任何类型,只要它通过当前的松散评估。

2 个答案:

答案 0 :(得分:1)

要将它们强制为字符串(不是数组或结构),请将accessor=true<cfproperty>一起使用并使用setter。

但是,如果你所说的SerializeJSON()将数字字符串视为整数,那么type="string"可以强制执行。它与SerializeJSON()函数的行为有关。如果您真的想将它们强制为字符串,请尝试https://github.com/bennadel/JsonSerializer.cfc

答案 1 :(得分:1)

据我了解,内置访问器设置器将进行自动类型验证,但只会在必要时进行的投射。

数字/字符串/日期/布尔值都被认为是“简单”,这就是数字数据传递“字符串”类型验证的原因。因此,因为它通过了验证,所以跳过了转换。就个人而言,我更愿意,如果它做了更严格的验证,但这对于bugtracker来说是一个问题。

现在,如果必须确保只有实际的字符串数据可以进入这些属性,您可以覆盖生成的属性setter,以进行更严格的类型转换和/或验证(我只在Lucee上测试过这个:

/** Example.cfc */
component accessors=true {
    property type="string" name="thisId";
    property type="string" name="thatId";

    public function setThisId(required string newId) {
        // convert numeric value to string value
        if (isNumeric(newId)) {
            newId = toString(newId);

        // throw an exception for non-string/numeric values
        // !isSimpleValue() is a catch-all btw, structs and arrays will
        // be prevented by the "newId" argument's type hint
        } else if (isBoolean(newId) || isDate(newId) || !isSimpleValue(newId)) {
            throw(message="Invalid value specified for thisId");
        }

        variables.thisId = newId;
        return this;
    }
}

var example = new Example();

example.setThisId(54321);
example.setThatId(54321);
writeoutput(serializeJson(example)); //{"thisId":"54321","thatId":54321}

// throws exceptions:
example.setThisId(true);
example.setThisId({});

最后,回到“必要的可能”的部分。对于给定的示例,如果您尝试将组件实例传递给setThisId()方法,则它将失败类型验证步骤,这意味着类型转换是必需,以使操作成功。然后检查值是否为类型转换的可能性。如果组件(这仅适用于Railo / Lucee)定义了_toString()“魔术方法”,那么类型转换是可能的。由于可能,然后将组件强制转换为字符串,然后将结果传递到setThisId()。如果未在组件上定义该魔术方法,则无法进行类型转换,并抛出异常。类似地,对于结构/数组,类型转换是必需的,但不是可能的,因为没有为这些类型定义自动序列化,因此导致抛出异常。

TL; DR

您可以覆盖setter访问器以执行更严格的类型验证/类型转换。

相关问题