将一个大型String流式传输到JAXB中

时间:2013-11-18 17:25:16

标签: java xml jaxb

我的JAXB层次结构中有一个域对象,必须用逗号分隔的值文本表示。不幸的是,明确构建CSV String的成本非常高,因此不是一种选择。

我创建了一个自定义@XmlJavaTypeAdapter,返回DataHandler(根据supported data types),但始终将数据写入BASE64 ...但我有一个遗留API来保留期待那里的ASCII字符串。更改DataHandler的MIME不会更改编码,但会影响XSD对其中包含的对象的定义。

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

我还考虑过返回Object(实际上是CharacterData)但需要实现public String getData() ...要求我明确构建String我我试图传播。

2 个答案:

答案 0 :(得分:1)

如果没有人提出DataHanler相关的解决方案......以下只是“解决方法”的另一个想法,它不涉及DataHandler。它需要访问编组人员。

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

  • 定义一个XMLStreamWriter包装器,如下所示:JAXB marshalling XMPP stanzas。覆盖writeStartElementwriteCharacters以拦截CSV元素的startElement调用以及紧随其后的writeCharacters

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

答案 1 :(得分:0)

我不太明白为什么显式构造CSV字符串(使用StringBuilder)比使用JAXB内置代码更昂贵。

如果性能是您的限制因素,那么我认为您应该考虑创建自定义序列化程序(例如,基于StringBuilder)和SAX处理程序来解析XML。

如果您可以轻松更改协议,那么您可能需要查看Grizzly frameworkAvroGoogle 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 &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 ]);
    }
}

除非我完全误解你的问题,否则这应该让你继续前进。