gpt4 book ai didi

java - 如何在不使用 setter 或公开变量的情况下将私有(private)类实例变量设置为解码器使用的 File 对象?

转载 作者:行者123 更新时间:2023-12-02 02:44:09 25 4
gpt4 key购买 nike

问题

我正在编写一个处理 XML 文件的 Java Swing 应用程序,因此我使用 JAXB 将类编码到文档中,并以相反的方式解码。

我想在编码的类中包含一个私有(private)字段,该字段以 File 对象的形式存储该类所基于的支持文件(如果有)。通过这种方式,我可以确定是否正在使用后备文件,以便在通过“Save”命令保存时,如果后备文件可用,我可以直接将类编码到该文件,而不是通过“保存文件”对话框获取它。

但是,使用 JAXB 中提供的工具,我在打开文件时似乎无法从 Unmarshaller 获取 File 对象。我该如何解决这种情况以便正确设置该变量?

由于此变量是内部变量,因此我不想包含 setter 或公开它,以便其他类无法更改它。

背景

了解class event callbacksexternal listeners ,我知道我可以在解码之前或之后使用类事件回调来设置类实例私有(private)字段,但似乎我无法从其中检索 Unmarshaller 正在使用的文件对象回调。

另一方面,通过外部监听器,我可以获取正在解码的 File 对象,因为它与 unmarshal 方法调用处于同一级别,但现在私有(private)字段要么需要公开,要么必须包含一个 setter 才能进行设置。

示例代码

以下是一个最小的、可重现的示例,分为两个文件:JAXBTest.javaMarshalMe.java,两者都放置在同一级别。

MarshalMe.java

import java.io.File;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class MarshalMe {
private File backingFile;

public File getBackingFile() {
return backingFile;
}

// Dummy function that sets the backing file beforehand.
public void processSth() {
backingFile = new File("dummy.hai");
}
}

JAXBDemo.java

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

public class JAXBTest {
public static void writeXML(MarshalMe me, File xml) {
try {
JAXBContext contextObj = JAXBContext.newInstance(MarshalMe.class);
Marshaller marshallerObj = contextObj.createMarshaller();
marshallerObj.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

marshallerObj.marshal(me, new FileOutputStream(xml));
} catch (JAXBException jaxbe) {
jaxbe.printStackTrace();
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
}
}

public static MarshalMe readXML(File xml) {
MarshalMe me = null;

try {
JAXBContext contextObj = JAXBContext.newInstance(MarshalMe.class);
Unmarshaller unmarshallerObj = contextObj.createUnmarshaller();

me = (MarshalMe) unmarshallerObj.unmarshal(xml);
} catch (JAXBException jaxbe) {
jaxbe.printStackTrace();
}

return me;
}

public static void main(String[] args) {
MarshalMe src = new MarshalMe();
src.processSth();
System.out.println(src.getBackingFile());

File meFile = new File("me.xml");
writeXML(new MarshalMe(), meFile);

MarshalMe retrieved = readXML(meFile);
System.out.println(retrieved.getBackingFile());
}
}

预期输出

使用 Java 1.8(或更高版本,提供 JAXB 库和运行时实现可访问)运行,最小的、可重现的示例输出:

dummy.hai
null

当我期望输出时

dummy.hai
me.xml

因为该类最初是在读回之前写入名为 me.xml 的 XML 文件中。

最佳答案

我找到了一种设置私有(private)字段而不暴露它或给它 setter 的方法:Reflection .

使用外部事件监听器,我可以获取 File 对象。然后,在 beforeUnmarshal 方法内,检查是否获得了正确的对象后,我使用反射来获取私有(private)字段,并使用 setAccessible 方法,我现在可以控制当我仅使用反射访问该字段时。

解除访问检查后,只需通过设置编辑该值,然后恢复检查即可。

相关变更

以下代码片段包含相关更改:

unmarshallerObj.setListener(new Unmarshaller.Listener() {
@Override
public void beforeUnmarshal(Object target, Object parent) {
if (!(target instanceof MarshalMe))
return;

MarshalMe me = (MarshalMe) target;
try {
Field meBackingFile = MarshalMe.class.getDeclaredField("backingFile");
meBackingFile.setAccessible(true);
meBackingFile.set(me, xml);
meBackingFile.setAccessible(false);
} catch (NoSuchFieldException nsfe) {
} catch (IllegalAccessException iae) {}
}
});

在示例程序中包含编辑

编辑文件JAXBDemo.java,添加以下代码:

// Add to the import section
import java.lang.reflect.Field;

// Under this function
public static MarshalMe readXML(File xml) {
MarshalMe me = null;

try {
JAXBContext contextObj = JAXBContext.newInstance(MarshalMe.class);
Unmarshaller unmarshallerObj = contextObj.createUnmarshaller();

/* Add this code vvv */
unmarshallerObj.setListener(new Unmarshaller.Listener() {
@Override
public void beforeUnmarshal(Object target, Object parent) {
if (!(target instanceof MarshalMe))
return;

MarshalMe me = (MarshalMe) target;
try {
Field meBackingFile = MarshalMe.class.getDeclaredField("backingFile");
meBackingFile.setAccessible(true);
meBackingFile.set(me, xml);
meBackingFile.setAccessible(false);
} catch (NoSuchFieldException nsfe) {
} catch (IllegalAccessException iae) {}
}
});
/* Add this code ^^^ */

me = (MarshalMe) unmarshallerObj.unmarshal(xml);
} catch (JAXBException jaxbe) {
jaxbe.printStackTrace();
}

return me;
}

/* Add this code */ 行之间添加 import 和代码后,再次运行程序现在输出:

dummy.hai
me.xml

正如预期的那样。

关于java - 如何在不使用 setter 或公开变量的情况下将私有(private)类实例变量设置为解码器使用的 File 对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57158170/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com