преобразование типов perl, javascript, JSON.parse JSON :: XS - нужны идеи - PullRequest
7 голосов
/ 13 февраля 2012

Я застрял в проблеме и ищу идеи относительно лучшего способа ее решения.

Я занялся разработкой сайта, на котором бэкэнд написан на Perl, а во внешнем интерфейсе широко используется javascript.

Клиент регулярно получает обновление нескольких сотен отслеживаемых объектов из серверной части. Объекты отображаются на карте Google с помощью JavaScript. Хеш объекта (анализируемый JavaScript) содержит много информации об объекте, например, местоположение, описание и различные переменные состояния.
Некоторые данные представлены в виде строк, а некоторые числовыми.

Проблема в том, что в процессе отправки данных на клиентский JavaScript все значения становятся строками. И так, в javascript, если я проверяю, например, чтобы увидеть, является ли значение положительным, тест проходит успешно, даже если значение равно 0, потому что значение на самом деле «0», а не 0.

На стороне сервера данные кодируются с помощью JSON :: XS следующим образом;

sub process_json {
my $self = shift;

return if( $self->{processed} );
$self->{processed} = 1;
if( $self->{requestrec}->status =~ /^3/ )
{
    return $self->{requestrec}->status;
}

$self->{data}->{session} = {
    id   => $self->session->id,
    user => $self->session->{data}->{__user},
};

use utf8;
my $output;
eval { $output = JSON::XS->new->utf8->encode( $self->{data} ); };
if($@)
{
    use Carp;
    confess($@);
}

$self->discard_request_body();
$self->req->content_type('application/json; charset=utf-8');
$self->req->headers_out->set( 'Expires' => 'Thu, 1 Jan 1970 00:00:00 GMT' );

my $out_bytes = encode( 'UTF-8', $output );
$self->req->headers_out->set( 'Content-Length' => length $output );
$self->req->print($output) unless( $self->req->header_only() );

delete $self->{session}->{data}->{errors}
    if( exists $self->{session}->{data}->{errors} );
delete $self->{session}->{data}->{info}
    if( exists $self->{session}->{data}->{info} );
} ## end of: sub process_json

На стороне клиента данные декодируются с помощью JSON.parse следующим образом:

function http_process (req, callback, errorfn) {
if (req.readyState == 4) {
    this.activerequest = null;
    if (req.status != 200 && req.status != 0 && req.status != 304 && req.status != 403) {
        if (errorfn) { return errorfn(req); } else { dialogue("Server error", "Server error " + req.status); }
        if (req.status == 400) { dialogue("ERROR","Session expired"); this.failed = 1;
        } else if (req.status == 404) { dialogue("ERROR", "Server error (404)"); this.failed = 1;
        } else if (req.status == 500) { dialogue("ERROR", "Server error (500)"); this.failed = 1; }
    } else {
        if (callback) {
            if (req.responseText) {
                lstatus("Loaded (" + req.responseText.length + " bytes)");
                warn("Received " + req.responseText.length + " bytes");
                try {
                    var obj = JSON.parse(req.responseText);
                } catch(e) {
                    warn(e);
                    warn(req.responseText);
                }
                callback( obj );
            } else {
                callback({});
            }
        }
    }
}

}

Может кто-нибудь увидеть полезный способ решения этой проблемы? Perl не является строго типизированным, и JSON :: XS просто анализирует числовые данные в виде строки, а не числа. Я попытался добавить reviver в JSON.parse (см. Код ниже) - однако он не работает (он обрабатывает большую часть данных правильно, но продолжает работать с различными сообщениями, такими как non_object_property_call или undefined_method.

Независимо от того, было бы предпочтительно, если бы работа парсинга данных для обеспечения правильного типа была обработана на стороне сервера. Кто-нибудь может увидеть, как это может быть эффективно достигнуто?

function toNum(key, value) {
/*make sure value contains an integer or a float not a string */
/*TODO: This seems VERY inefficient - is there a better way */
if (value instanceof Object) return;
if (value instanceof Array) return;
var intRE = /^\d+$/;
var floatRE = /^\d*\.\d+$/;
if(value.match(intRE)) {
    value = parseInt(value);
    return value;
}
if(value.match(floatRE)) {
    value = parseFloat(value);
    return value;
}   

}

1 Ответ

7 голосов
/ 13 февраля 2012

Простые скаляры Perl (любой скаляр, который не является ссылкой) являются наиболее сложными объектами для кодирования: JSON :: XS и JSON :: PP будут кодировать неопределенные скаляры как нулевые значения JSON, скаляры, которые в последний раз использовались в строке контекст перед кодированием в виде строк JSON и все остальное как числовое значение ...

Хитрость заключается в том, чтобы «нумеровать» любое значение, которое вы хотите закодировать как число, непосредственно перед его кодированием.

$ perl -MJSON -e '$x="5"; print JSON->new->encode( [ $x, "$x", 0+$x ] )'
["5","5",5]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...