序列化所需元素时,避免"empty element tags"的正确方法是什么?
示例:
@ElementList(name="rpc", required=true)
public ArrayList<FunctionRequest> getRequestedFunctions() {
return requestedFunctions;
}
空列表将导致引用的&#34;空元素标记中的单个XML元素&#34;式:
<rpc/>
我实际需要的是以下表示:
<rpc></rpc>
编辑:我需要一个不同类型列表的解决方案!例如,可能还有List<String>
,List<int>
,List<WhateverClass>
,... PLUS注释的不同名称属性,即&#34; rpc&#34;在示例中。
感谢您的帮助。
我将首先介绍我实际实施的解决方案以及我在我的项目中使用的解决方案。之后,我将提出一个更改建议,可以直接添加到简单代码中。
由于我不想手动定义XML元素并为我的程序中的每个List创建一个Converter,我决定使用我的类的现有注释,以及名称属性。注释。如果使用getter和setter,我的解决方案将适用于将被序列化的类!它目前只绑定到使用Attribute
和Text
注释的类!
为了更灵活,我创建了一个&#34; base&#34;班RequestListConverter
。它有两个protected
方法prepareMethodList
和writeRequest
。 prepareMethodList
将使用反射遍历给定类的所有方法,并创建方法 - 注释 - 映射。然后,writeRequest
会将给定方法prepareMethodList
的类型的单个对象写入到write
接口的Converter
方法中给出的Simple的OutputNode。
public class RequestListConverter {
private HashMap<Method, Object> curMethodAnnotationMap = new HashMap<Method, Object>();
@SuppressWarnings("rawtypes")
protected void prepareMethodList(Class targetClass) {
/*
* First, get the annotation information from the given class.
*
* Since we use getters and setters, look for the "get" methods!
*/
Method[] curMethods = targetClass.getMethods();
for (Method curMethod : curMethods) {
String curName = curMethod.getName();
// We only want getter methods that return a String
if (curName.startsWith("get") && (curMethod.getReturnType() == String.class)) {
Attribute curAttrAnnotation = curMethod.getAnnotation(Attribute.class);
Text curTextAnnotation = curMethod.getAnnotation(Text.class);
if (curAttrAnnotation != null) {
curMethodAnnotationMap.put(curMethod, curAttrAnnotation);
} else
if (curTextAnnotation != null) {
curMethodAnnotationMap.put(curMethod, curTextAnnotation);
}
}
}
}
protected void writeRequest(OutputNode curNode, Object curRequest) throws Exception {
for (Map.Entry<Method, Object> curMapEntry : curMethodAnnotationMap
.entrySet()) {
if ((curMapEntry.getKey() == null)
|| (curMapEntry.getValue() == null)) {
continue;
}
Method curMethod = curMapEntry.getKey();
Attribute curAttrAnnotation = null;
Text curTextAnnotation = null;
if (curMapEntry.getValue() instanceof Attribute) {
curAttrAnnotation = (Attribute) curMapEntry.getValue();
} else if (curMapEntry.getValue() instanceof Text) {
curTextAnnotation = (Text) curMapEntry.getValue();
} else {
continue;
}
String curValue = null;
try {
// Try to invoke the getter
curValue = (String) curMethod.invoke(curRequest);
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
// The getter method seems to need any argument, strange! Skip
// this!
continue;
}
// If the method has an Attribute annotation, then ...
if (curAttrAnnotation != null) {
boolean curAttrRequired = curAttrAnnotation.required();
String curAttrName = curAttrAnnotation.name();
/*
* IF the returned method value is NULL THEN IF if the attribute
* is required THEN throw a NullPointerException, ELSE skip the
* attribute
*/
if (curValue == null) {
if (curAttrRequired) {
throw new NullPointerException(
"The required attribute " + curAttrName
+ " returned NULL!");
} else {
continue;
}
}
// The attribute will be added as XML text now
curNode.setAttribute(curAttrName, curValue);
} else
// If the method has a Text annotation, then ...
if (curTextAnnotation != null) {
// we only need to store it for later string creation
curNode.setValue(curValue);
}
}
curNode.commit();
}
}
基于这个类,我创建了 - 例如 - 类SetRequestListConverter
。它实现了Simple Converter
接口,因此它提供了未实现的方法read
和write
,它获取了可能包含元素或可能为空的List。
该示例显示了类SetRequestList
的Converter的实现。它扩展了先前引入的基类RequestConverter
类,并实现了Converter
类型的SetRequestList
。
public class SetRequestListConverter extends RequestListConverter implements Converter<SetRequestList> {
@Override
public SetRequestList read(InputNode newNode) throws Exception {
return null;
}
@Override
public void write(OutputNode newNode, SetRequestList newValue) throws Exception {
if (newValue.requests.isEmpty()) {
newNode.setValue("");
return;
}
this.prepareMethodList(SetRequest.class);
/*
* Now we can go through all SetRequests and call the methods
* to build the XML attributes and to get the element value (i.e. parameters)
*/
for (SetRequest curRequest : newValue.requests) {
OutputNode curNode = newNode.getChild("set");
this.writeRequest(curNode, curRequest);
}
}
}
使用过的SetRequestList
是一个包含ArrayList<SetRequest>
的简单类。这需要隐藏这实际上是一个ArrayList的事实。
@Root
@Convert(SetRequestListConverter.class)
public abstract class SetRequestList {
protected ArrayList<SetRequest> requests = new ArrayList<SetRequest>();
public void add(T newRequest) {
requests.add(newRequest);
}
}
然后可以像这样使用这个类:
public class ClassToSerialize {
private SetRequestList requestedSets = new SetRequestList();
@Element(name="get", required=true)
public SetRequestList getRequestedSets() {
return requestedSets;
}
@Element(name="get", required=true)
public void setRequestedSets(SetRequestList newRequestedSets) {
requestedSets = newRequestedSets;
}
}
生成的包含SetRequestList
元素的XML将如下所示:
<get>
<set someAttribute="text" anotherAttribute="bla">Some Text</set>
...
</get>
生成的SetRequestList
为空的XML将如下所示:
<get></get>
是的,正是我所需要的事实,我可以继续使用SetRequest
或任何类别的注释!无需再次(重新)定义XML结构!
注意:这只是一个解决方案提案,尚未经过测试!
我查看了Simple的源代码,发现Formatter
类实际上是在编写开始和结束标记,以及空元素标记。它是通过移交Format
对象创建的。简单的Javadoc描述了Format
类,如下所示:
Format对象用于提供有关如何构建生成的XML文档的信息。
因此,如果应该创建空元素标签,可以使用信息进行扩展。为此,我添加了私有变量useEmptyEndTag
以及相应的getter和setter方法。变量将在构造函数中使用true
进行初始化。如果空结束标记样式不是应该创建的样式,则可以在使用Format
创建myFormat.setUseEmptyEndTag(false)
对象后设置它。
Formatter
类通过保存给定Format
对象的新私有变量得到增强,以便能够在适当的代码位置访问设置参数。空结束标记写在writeEnd
内。看看官方源代码看原始代码。以下是我的建议是避免空元素标记:
public void writeEnd(String name, String prefix) throws Exception {
String text = indenter.pop();
// This will act like the element contains text
if ((last == Tag.START) && (!format.isUseEmptyEndTag())) {
write('>');
last = Tag.TEXT;
}
if (last == Tag.START) {
write('/');
write('>');
} else {
if (last != Tag.TEXT) {
write(text);
}
if (last != Tag.START) {
write('<');
write('/');
write(name, prefix);
write('>');
}
}
last = Tag.END;
}
这个提议是更好的解决方案 - 在我看来 - 我希望它会被添加到Simple的源代码中。
答案 0 :(得分:0)
正如baraky之前所写的那样,您可以使用<rpc></rpc>
标签解决部件,并在此处发布转换器:Prevent inclusion of empty tag for an empty ElementList in an ElementListUnion。
ExampleConverter
对此特殊部分进行了评论。
此外,要获取name
属性,请参阅此处:How do you access field annotations from a custom Converter with Simple?
所以你需要的是Converter
- 你班级的实施。对于类型(int
,String
等),请查看Transformer
- 类。