Java 将大字符串流式传输到JAXB

Java 将大字符串流式传输到JAXB,java,xml,jaxb,Java,Xml,Jaxb,我的JAXB层次结构中有一个域对象,它必须表示为逗号分隔的值文本。不幸的是,显式构造CSV字符串的成本非常高,因此这不是一个选项 我创建了一个自定义的@XmlJavaTypeAdapter,它返回一个数据处理程序(根据),但它总是将数据写入BASE64中。。。但我要保留一个遗留API,它需要ASCII字符串。更改DataHandler的MIME不会更改编码,但会影响XSD中包含的对象的定义 有没有办法设置DataHandler(或任何其他支持的Java类型)以从流式输入返回未编码的字符串 我还考

我的JAXB层次结构中有一个域对象,它必须表示为逗号分隔的值文本。不幸的是,显式构造CSV
字符串
的成本非常高,因此这不是一个选项

我创建了一个自定义的
@XmlJavaTypeAdapter
,它返回一个
数据处理程序
(根据),但它总是将数据写入BASE64中。。。但我要保留一个遗留API,它需要ASCII字符串。更改
DataHandler
的MIME不会更改编码,但会影响XSD中包含的对象的定义

有没有办法设置
DataHandler
(或任何其他支持的Java类型)以从流式输入返回未编码的
字符串


我还考虑返回一个
对象(实际上是
CharacterData
),但这需要实现
公共字符串getData()
。。。需要我显式地构造我正在尝试流式传输的
字符串。

以防没有人提出与
DataHanler
相关的解决方案。。。以下只是一个不涉及
DataHandler
的“解决方案”的替代方案。它需要访问封送员

  • 修改XML类型适配器以不返回内容,而是返回一种短地址以获取流数据(例如文件名)

  • 定义如下所示的XMLStreamWriter包装器:。覆盖
    writeStarElement
    writeCharacters
    以拦截对CSV元素的
    startElement
    调用以及紧接其后的
    writeCharacters

  • 传递给特定调用
    writeCharacters
    的数据将是获取流数据的地址。将其分块流式传输到包装好的XMLStreamWriter的WriteCharacter


以防没有人提出与DataHanler相关的解决方案。。。以下只是一个不涉及
DataHandler
的“解决方案”的替代方案。它需要访问封送员

  • 修改XML类型适配器以不返回内容,而是返回一种短地址以获取流数据(例如文件名)

  • 定义如下所示的XMLStreamWriter包装器:。覆盖
    writeStarElement
    writeCharacters
    以拦截对CSV元素的
    startElement
    调用以及紧接其后的
    writeCharacters

  • 传递给特定调用
    writeCharacters
    的数据将是获取流数据的地址。将其分块流式传输到包装好的XMLStreamWriter的WriteCharacter


我不太明白为什么显式构造CSV字符串(使用StringBuilder)要比使用JAXB内置的成本更高

如果性能是您的限制因素,那么我认为您应该考虑创建自定义序列化器(例如,基于StrugBueDever)和SAX处理程序来解析XML。p> 如果您有权更改协议,那么您可能想签出,并且-它们的维护要多一些,但是如果您追求的是性能,那么这些应该更快

和往常一样,您应该使用这两种方法进行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 &amp;&lt;&gt;'\"</FirstField>"
                          + "<Second>&lt;!CDATA[[ &lt;!-- now we're just nasty --&gt; ]]&gt;</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 ]);
    }
}
导入静态org.junit.Assert.assertEquals;
导入java.io.StringWriter;
导入javax.xml.bind.JAXBContext;
导入javax.xml.bind.JAXBException;
导入javax.xml.bind.annotation.XmlAccessType;
导入javax.xml.bind.annotation.XmlAccessorType;
导入javax.xml.bind.annotation.xmlement;
导入javax.xml.bind.annotation.XmlRootElement;
导入javax.xml.bind.annotation.adapters.XmlAdapter;
导入javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
导入org.junit.Test;
公开课范例
{
公共字符串序列化(DataObject d)引发JAXBEException{
StringWriter缓冲区=新StringWriter();
newInstance(DataObject.class).createMarshaller().Marshall(d,缓冲区);
返回buffer.toString();
}
@试验
public void testSerialize()抛出jaxbeexception{
字符串应为“”
+“具有特殊字符的字段1内容(&;”\“”
+“!CDATA[[!--现在我们很讨厌--]”
+“a、b、c”;
assertEquals(应为序列化(新数据对象()).replaceAll((\r)?\n(\r)?“,”\n”);
}
}
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
类数据对象
{
@xmlement(name=“FirstField”)
私有最终字符串field1=“带有特殊字符的field1内容&'\”;
@xmlement(name=“Second”)
专用最终字符串字段2=“]]>”;
@xmlement(name=“自定义”)
@XmlJavaTypeAdapter(值=CustomAdapter.class)
//您可以将其移到类型上
私有最终定制类型=新定制类型(“a”、“b”、“c”);
}
@XmlAccessorType(XmlAccessType.FIELD)
类自定义类型
{
私人最终字符串a;
私有最终字符串b;
私有最终字符串c;
公共自定义类型(字符串a、字符串b、字符串c){
这个a=a;
这个.b=b;
这个.c=c;
}
公共字符串getA(){
返回a;
}
公共字符串getB(){
返回b;
}
公共字符串getC(){
返回c;
}
}
类CustomAdapter扩展了XmlAdapter
{
@凌驾
公共字符串封送处理(CustomType v)引发异常{
返回String.format(“%s,%s,%s”,v.getA(),v.getB(),v.getC());
}
@凌驾
/**请不要在产品中使用此选项:>*/
公共CustomType解组(字符串v)引发异常{
字符串[]split=v.split(“,”);
返回新的CustomType(拆分)