我的JAXB层次结构中有一个域对象,必须用逗号分隔的值文本表示。不幸的是,明确构建CSV String
的成本非常高,因此不是一种选择。
我创建了一个自定义@XmlJavaTypeAdapter
,返回DataHandler
(根据supported data types),但始终将数据写入BASE64 ...但我有一个遗留API来保留期待那里的ASCII字符串。更改DataHandler
的MIME不会更改编码,但会影响XSD对其中包含的对象的定义。
有没有办法设置DataHandler
(或任何其他受支持的Java类型)从流式输入返回未编码的String
?
我还考虑过返回Object
(实际上是CharacterData
)但需要实现public String getData()
...要求我明确构建String
我我试图传播。
答案 0 :(得分:1)
如果没有人提出DataHanler
相关的解决方案......以下只是“解决方法”的另一个想法,它不涉及DataHandler
。它需要访问编组人员。
修改XML类型适配器以不返回内容,而是返回一种短地址来获取流数据(例如文件名)。
定义一个XMLStreamWriter包装器,如下所示:JAXB marshalling XMPP stanzas。覆盖writeStartElement
和writeCharacters
以拦截CSV元素的startElement
调用以及紧随其后的writeCharacters
。
传递给writeCharacters
特定调用的数据将是获取流数据的地址。将它以块的形式传输到包装好的XMLStreamWriter的writeCharacters。
答案 1 :(得分:0)
我不太明白为什么显式构造CSV字符串(使用StringBuilder)比使用JAXB内置代码更昂贵。
如果性能是您的限制因素,那么我认为您应该考虑创建自定义序列化程序(例如,基于StringBuilder)和SAX处理程序来解析XML。
如果您可以轻松更改协议,那么您可能需要查看Grizzly framework,Avro和Google ProtoBuf - 对它们进行更多维护,但如果您在表现之后,这些应该更快。
与往常一样,在将任何内容设置为石头之前,您应该使用这两种方法进行A / B性能测试;)
回到原始主题,这是一个关于如何使用自定义适配器的示例:
import static org.junit.Assert.assertEquals;
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.junit.Test;
public class Example
{
public String serialize( DataObject d ) throws JAXBException {
StringWriter buffer = new StringWriter();
JAXBContext.newInstance(DataObject.class).createMarshaller().marshal(d, buffer);
return buffer.toString();
}
@Test
public void testSerialize( ) throws JAXBException {
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><dataObject>"
+ "<FirstField>field1 content with special characters &<>'\"</FirstField>"
+ "<Second><!CDATA[[ <!-- now we're just nasty --> ]]></Second>"
+ "<Custom>a,b,c</Custom></dataObject>";
assertEquals(expected, serialize(new DataObject()).replaceAll("(\r)?\n(\r)?", "\n"));
}
}
@XmlRootElement
@XmlAccessorType( XmlAccessType.FIELD )
class DataObject
{
@XmlElement( name = "FirstField" )
private final String field1 = "field1 content with special characters &<>'\"";
@XmlElement( name = "Second" )
private final String field2 = "<!CDATA[[ <!-- now we're just nasty --> ]]>";
@XmlElement( name = "Custom" )
@XmlJavaTypeAdapter( value = CustomAdapter.class )
// you can move this over the type
private final CustomType type = new CustomType("a", "b", "c");
}
@XmlAccessorType( XmlAccessType.FIELD )
class CustomType
{
private final String a;
private final String b;
private final String c;
public CustomType( String a, String b, String c ) {
this.a = a;
this.b = b;
this.c = c;
}
public String getA( ) {
return a;
}
public String getB( ) {
return b;
}
public String getC( ) {
return c;
}
}
class CustomAdapter extends XmlAdapter<String, CustomType>
{
@Override
public String marshal( CustomType v ) throws Exception {
return String.format("%s,%s,%s", v.getA(), v.getB(), v.getC());
}
@Override
/** Please don't use this in PROD :> */
public CustomType unmarshal( String v ) throws Exception {
String[] split = v.split(",");
return new CustomType(split[ 0 ], split[ 1 ], split[ 2 ]);
}
}
除非我完全误解你的问题,否则这应该让你继续前进。