我正在尝试编写一个类,该类可以使用Java对XML的设置进行序列化和反序列化。我已经用C#成功编写了这段代码,它非常有用,因此我想在我的Java应用程序中找到类似的东西。
我有以下基类,每个要序列化为XML的类都必须实现。
package serializers;
import java.lang.reflect.ParameterizedType;
abstract class XmlSerializableObject<T> {
abstract T getDefault();
abstract String getSerializedFilePath();
String getGenericName() {
return ((Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0]).getTypeName();
}
ClassLoader getClassLoader() {
return ((Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0]).getClassLoader();
}
}
其中getGenericName
和getClassLoader
用于实例化JAXBContext
。然后,我将其作为设置提供程序
public class SettingsProvider extends XmlSerializableObject<SettingsProvider> {
private Settings settings;
@Override
public SettingsProvider getDefault() {
return null;
}
@Override
public String getSerializedFilePath() {
return "C:\\Data\\__tmp.settings";
}
public Settings getSettings() {
return settings;
};
public void setSettings(Settings settings) {
this.settings = settings;
}
}
class Settings {
private String tmp;
public String getTmp() {
return tmp;
}
public void setTmp(String tmp) {
this.tmp = tmp;
}
}
现在我有以下序列化程序类
package serializers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.File;
public class XmlSerializer {
private static final Logger logger = LoggerFactory.getLogger(XmlSerializer.class);
public static <T extends XmlSerializableObject> void Serialize(T o) {
String filePath = o.getSerializedFilePath();
File file = new File(filePath);
try {
String name = o.getGenericName();
ClassLoader classLoader = o.getClassLoader();
// THE FOLLOWING LINE throws.
JAXBContext jaxbContext = JAXBContext.newInstance(name, classLoader); // also tried JAXBContext.newInstance(name);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(o, file);
} catch (JAXBException e) {
logger.error("Serialization failed", e);
}
}
// Deserialize below.
}
然后我进行以下测试以检查序列化结果
package serializers;
import org.junit.Before;
import org.junit.Test;
public class XmlSerializerTest {
private Settings settings = new Settings();
private SettingsProvider provider;
@Before
public void setUp() throws Exception {
settings.setTmp("testing");
provider = new SettingsProvider();
provider.setSettings(settings);
}
@Test
public void serialize() throws Exception {
XmlSerializer.Serialize(provider);
}
}
问题在于调用JAXBContext jaxbContext = JAXBContext.newInstance(name, classLoader);
会引发
javax.xml.bind.JAXBException:无法实例化提供程序com.sun.xml.internal.bind.v2.ContextFactory:javax.xml.bind.JAXBException:“ serializers.SettingsProvider”不包含ObjectFactory.class或jaxb 。指数 -具有链接的例外: [javax.xml.bind.JAXBException:“ serializers.SettingsProvider”不包含ObjectFactory.class或jaxb.index]
我尝试使用ClassLoader
对象和不使用 For lngCounter = lngLastrowresults To 3 Step -1
On Error GoTo Handler
If IsError(Application.Match(Range(Mid(strProjLoc, 2, 1) & CStr(lngCounter)), arrProjectID, False)) Then
Range("B" & CStr(lngCounter)).EntireRow.Delete
Debug.Print lngCounter
End If
Next lngCounter
对象都无济于事。如何以这种方式序列化通用类型?
感谢您的时间。
答案 0 :(得分:4)
让我们看看引发异常的代码行:
JAXBContext jaxbContext = JAXBContext.newInstance(name);
在上面的代码行中,您传递的参数name
是要反序列化的类的名称,并在运行时确定(即给定示例中的serializers.SettingsProvider
) )。这可能不足以让JAXB确定构成JAXB上下文的所有类。因此,请尝试传递包含JAXBContext
实例应反序列化的所有类的软件包名称-该软件包中的所有类都是您的JAXB上下文。这是在编译时会知道的东西。因此,请尝试以下代码行:
JAXBContext jaxbContext = JAXBContext.newInstance("serializers");
在这里,“ serializers”是包含要反序列化的所有类的程序包的名称,即给定示例的 JAXB上下文。
您可能希望参考Oracle JAXB tutorial并注意以下代码行:
import primer.po.*;
...
JAXBContext jc = JAXBContext.newInstance( "primer.po" );
请参阅此Javadoc,并请注意,如果要反序列化的类分散在多个包中,则应传递由冒号分隔的包名称列表,例如,-
JAXBContext.newInstance( "com.acme.foo:com.acme.bar" )
如果您必须传递类名而不是包名,那么首先请非常仔细地阅读此Javadoc。请注意,JAXBContext
实例将仅使用作为参数传递的类以及从这些类静态可访问的类进行初始化。最好以在编译时就知道要传递的类名的方式编写程序。
另外,您可能会注意到Java中的泛型(特别是类型为erasure的泛型)与C#中的泛型有所不同,请参阅What is the concept of erasure in generics in Java?。
同样,给定类声明:
class XmlSerializableObject<T> {
}
声明类XmlSerializableObject
处理类型T
,并带有以下类声明:
class SettingsProvider extends XmlSerializableObject<SettingsProvider> {
}
其中指出类SettingsProvider
处理其自身的类型声音是复杂的。
或者您改为按如下方式声明它:
class SettingsProvider extends XmlSerializableObject<Settings> {
}
其中指出类SettingsProvider
处理的类型为Settings
?
答案 1 :(得分:2)
看起来应该是JAXBContext.newInstance(SettingsProvider.class)
。
该方法的JAXBContext.newInstance(String ...)
版本需要一个软件包名称,如错误消息所述,该软件包名称应包含一个ObjectFactory
类或jaxb.index
列表以将其引导至这些类。
答案 2 :(得分:2)
您正在使用this newInstance
method:
参数:
contextPath-包含模式派生类和/或Java到模式(JAXB注释)映射类的 java包名称列表
classLoader-该类加载器将用于查找实现类。
因此df778899是正确的,您不应使用此签名,因为getGenericName
返回的是完全合格的类名,而不是包。即使它是一个包裹,您仍然会错过ObjectFactory.class
或jaxb.index
但是JAXBContext.newInstance(SettingsProvider.class)
也不起作用。您将得到一个MarshalException
指示缺少@XmlRootElement
像这样注释SettingsProvider
:
@XmlRootElement(name = "root")
static class SettingsProvider extends XmlSerializableObject<SettingsProvider>
{
private Settings settings;
// [...]
最后您会得到:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<settings>
<tmp>testing</tmp>
</settings>
</root>
答案 3 :(得分:0)
这是通过使用以下接口完成的
public interface IXmlSerializableObject {
String getSerializedFilePath();
}
关键人物
public interface IPersistanceProvider<T> extends IXmlSerializableObject {
void save();
void restoreDefaults();
Class<T> getTypeParameterClass();
}
关键属性是Class<T> getTypeParameterClass()
。然后在
public static <T extends PersistanceProviderBase> void Serialize(T o) {
String filePath = o.getSerializedFilePath();
File file = new File(filePath);
try {
JAXBContext jaxbContext = JAXBContext.newInstance(o.getTypeParameterClass());
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(o, file);
} catch (JAXBException e) {
logger.error("Serialization failed", e);
}
}
其中PersistanceProviderBase
实现了IPersistanceProvider
接口。