记录PHPSerializer工具类反序列化遇到的坑

php语言中,有自己封装好的序列化跟反序列化的方法,如果我们在java项目中需要反序列化由php序列化的数据,我们可以使用PHPSerializer工具类

pom文件中引入依赖

<!-- 反序列化php序列化过的字符串 -->
<dependency>
    <groupId>org.sction</groupId>
    <artifactId>phprpc</artifactId>
    <version>3.0.2</version>
</dependency>

使用也非常简单,由于序列化的数据大多数是json,该工具会反序列化成AssocArray类对象,所以写法如下:

public static AssocArray unSerializePhpStr(String str) throws InvocationTargetException, IllegalAccessException {
        if(StringUtils.isEmpty(str)) {
            return null;
        }
        PHPSerializer p = new PHPSerializer();
        return (AssocArray) p.unserialize(str.getBytes());
}

我们反序列化一个简单的json字符串:a:1:{i:123;s:2:"id"}

String str = "a:1:{i:123;s:2:\"id\"}";
AssocArray object = PhpSerializerUtil.unSerializePhpStr(str);
HashMap hashMap = object.toHashMap();

反序列化成功了,我们可以使用toHashMap()将AssocArray对象转成HashMap对象

但是这只是简单的json反序列化,php在序列化的时候,数字类型都会序列化成i类型,而在java中会区分int和long类型,所以如果数值太大,在java中会反序列化失败,比如:

a:2:{i:1;i:1649729539760;s:2:"id";i:2;}

我们反序列化一下试试

 

发现直接报错,我们去看一下源码:

这里可以看到,i类型直接会映射成Integer,所以长度会有限制。我们再仔细看,发现有个d类型可以映射成Double,那我们将i全部改成d试试

str.replaceAll("i:","d:");

 再试一下

发现返回的直接是null,虽然没报错,但是明显不对,我们再看看源码:

 

 i跟d的映射应该没有问题,问题出现在a上面,我们进去看看

json是<k,v>结构,可以看到AssoArray对象的key并不支持Double类型,所以直接返回了null,所以只能将value类型由i转成d,key不能改,但是反序列化之后的字符串特别难区分key和value,写起来很麻烦,所以干脆,直接将i类型转成s(String),这样key和value都支持

public static AssocArray unSerializePhpStr(String str) throws InvocationTargetException, IllegalAccessException {
        if(StringUtils.isEmpty(str)) {
            return null;
        }
        //整形全部转成字符串,避免长度超出(key不支持其他数字类型)
        String numStr = "i:";
        while (str.contains(numStr)) {
            int index = str.indexOf(numStr);
            String str1 = str.substring(0,index);
            String str2 = str.substring(index);
            int index1 = str2.indexOf(";");
            str = String.format("%ss:%d:\"%s\"%s",str1,index1-2,str2.substring(2,index1),str2.substring(index1));
        }
        PHPSerializer p = new PHPSerializer();
        return (AssocArray) p.unserialize(str.getBytes());
    }

 我们再试试:

发现成功了

注意:AssocArray对象中的字符串类型value全都是byte数组,需要自己手动转成String

补充:AssocArray对象处理起来太麻烦,补充一个返回json的方法

/**
     * 将所有的AssocArray转成java
     * @param str
     * @return
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public static JSONObject unSerializePhpStrBackJson(String str) throws InvocationTargetException, IllegalAccessException {
        AssocArray assocArray = unSerializePhpStr(str);
        return assocArrayToHash(assocArray);
    }

    private static JSONObject assocArrayToHash(AssocArray assocArray) {
        HashMap hashMap = assocArray.toHashMap();
        JSONObject result = new JSONObject();
        for (Object key : hashMap.keySet()) {
            if(hashMap.get(key) instanceof AssocArray) {
                result.put(key.toString(),assocArrayToHash((AssocArray) hashMap.get(key)));
            }else if(hashMap.get(key) instanceof byte[]) {
                result.put(key.toString(),new String((byte[]) hashMap.get(key)));
            }else {
                result.put(key.toString(),hashMap.get(key));
            }
        }
        return result;
    }

***********************补充2022/11/18

1、如果序列化后的数据包含转义字符,这个时候会反序列化失败,我们需要去除转义字符

str = str.replaceAll("\\\\\"","\"");

2、如果php序列化的数据包含中文,php可能会将其序列化为16进制编码,比如下面的数据

a:1:{s:4:"name";s:6:"\xe5\x90\x8d\xe7\x89\x87";}

这个时候同样会反序列化失败,此时,我们需要将16进制编码转变成中文

public static String hex2UTF8(String hexStr) throws UnsupportedEncodingException {
        return URLDecoder.decode(hexStr.replaceAll("\\\\x", "%"), StandardCharsets.UTF_8.name());
    }

先调用这个方法去除一下16进制编码,然后再反序列化就可以了

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值