如何配置Mojo::XMLRPC以指定DateTime格式处理Mojo::Date?
Mojo::XMLRPC日期格式适配方案
问题背景
使用Mojo::XMLRPC时遇到两个日期格式相关问题:
- 编码问题:
encode_xmlrpc生成的<dateTime.iso8601>格式为2023-06-14T19:45:48Z,但业务需要无分隔符日期+带时区偏移的格式(如20230614T22:45:48+0300) - 解码问题:当输入的
<dateTime.iso8601>值为20230608T12:00:00+0000时,解码报错,错误信息如下:
Garbage at end of string in strptime: +0000 at /usr/local/opt/perl/lib/perl5/5.36/darwin-thread-multi-2level/Time/Piece.pm line 598. Perhaps a format flag did not match the actual input? at /usr/local/opt/perl/lib/perl5/5.36/darwin-thread-multi-2level/Time/Piece.pm line 598
问题根源在于Mojo::XMLRPC默认的日期格式数组不包含%Y%m%dT%H:%M:%S%z这类带时区偏移的无分隔符格式,导致编码输出不符合要求,解码无法识别目标格式。
解决方法
方案一:猴子补丁(推荐,无需修改模块源码)
通过重写Mojo::XMLRPC的编码/解码逻辑,适配自定义日期格式,完整代码如下:
#!/usr/bin/env perl use Mojo::XMLRPC qw[encode_xmlrpc decode_xmlrpc]; use Data::Dumper; use Mojo::Date; use Time::Piece; # 重写编码逻辑:生成自定义日期格式 BEGIN { no warnings 'redefine'; *Mojo::XMLRPC::_encode_value = sub { my ($value) = @_; if (ref $value eq 'Mojo::Date') { # 生成YYYYMMDDTHH:MM:SS+ZZZZ格式的日期字符串 my $formatted = $value->strftime('%Y%m%dT%H:%M:%S%z'); return "<dateTime.iso8601>$formatted</dateTime.iso8601>"; } # 保留原有其他类型编码逻辑 return Mojo::XMLRPC::_encode_value($value) unless ref $value; return '<array><data>' . join('', map { _encode_value($_) } @$value) . '</data></array>' if ref $value eq 'ARRAY'; return '<struct>' . join('', map { '<member><name>' . Mojo::XMLRPC::_escape_xml($_) . '</name>' . _encode_value($value->{$_}) . '</member>' } sort keys %$value) . '</struct>' if ref $value eq 'HASH'; return '<string>' . Mojo::XMLRPC::_escape_xml($value) . '</string>'; }; # 重写解码逻辑:支持解析带时区偏移的无分隔符日期 *Mojo::XMLRPC::_decode_value = sub { my ($value) = @_; if ($value =~ /^<dateTime\.iso8601>(.+)<\/dateTime\.iso8601>$/) { my $dt_str = $1; my $date; # 先尝试原有格式解析 eval { $date = Mojo::Date->new($dt_str) }; # 原有格式失败时,尝试自定义格式解析 if (!$date || $@) { eval { my $tp = Time::Piece->strptime($dt_str, '%Y%m%dT%H:%M:%S%z'); $date = Mojo::Date->new($tp->epoch); }; } die "Invalid dateTime.iso8601 value: $dt_str" unless $date; return $date; } # 保留原有其他类型解码逻辑 return $1 if $value =~ /^<i4>(.+)<\/i4>$/ || $value =~ /^<int>(.+)<\/int>$/; return $1 if $value =~ /^<string>(.+)<\/string>$/; return $1 + 0 if $value =~ /^<double>(.+)<\/double>$/; return 1 if $value eq '<boolean>1</boolean>'; return 0 if $value eq '<boolean>0</boolean>'; return undef if $value eq '<nil/>'; return [map { _decode_value($_) } $value =~ /<value>(.+?)<\/value>/sg] if $value =~ /^<array><data>(.+)<\/data><\/array>$/; return {map { /<name>(.+?)<\/name><value>(.+?)<\/value>/; $1 => _decode_value($2) } $value =~ /<member>(.+?)<\/member>/sg} if $value =~ /^<struct>(.+)<\/struct>$/; die "Unsupported XML-RPC value: $value"; }; } # 测试编码 my $e = encode_xmlrpc(call => 'TaregtMethod' => {originTimeStamp=>Mojo::Date->new(time)}); say STDERR Dumper($e); # 测试解码 my $xml = <<'XML'; <?xml version="1.0" encoding="utf-8"?> <methodResponse> <params> <param> <value> <struct> <member> <name>activationDate</name> <value> <dateTime.iso8601>20230608T12:00:00+0000</dateTime.iso8601> </value> </member> </struct> </value> </param> </params> </methodResponse> XML my $d = decode_xmlrpc($xml); say STDERR Dumper($d);
方案二:修改Mojo::XMLRPC模块源码(不推荐)
找到系统中Mojo/XMLRPC.pm文件,修改两个关键部分:
- 添加自定义日期格式到
@time_formats数组:
# 原代码 my @time_formats = ( '%Y-%m-%dT%H:%M:%SZ', '%Y-%m-%dT%H:%M:%S', '%Y-%m-%d' ); # 修改后 my @time_formats = ( '%Y-%m-%dT%H:%M:%SZ', '%Y-%m-%dT%H:%M:%S', '%Y-%m-%d', '%Y%m%dT%H:%M:%S%z' # 新增格式 );
- 修改编码时的日期生成逻辑,使用
strftime输出目标格式:
找到编码Mojo::Date的代码段,替换为:
if (ref $value eq 'Mojo::Date') { return "<dateTime.iso8601>" . $value->strftime('%Y%m%dT%H:%M:%S%z') . "</dateTime.iso8601>"; }
注意:该方案会在模块更新后丢失修改,仅适合临时场景使用。
内容的提问来源于stack exchange,提问作者h q
相关产品推荐
相关产品推荐

