问题描述
我需要将大量二进制数据存储到一个文件中,但我还想以 XML 格式读取/写入该文件的标题.
I need to store a huge amount of binary data into a file, but I want also to read/write the header of that file in XML format.
是的,我可以将二进制数据存储到某个 XML 值中,然后使用 base64 编码对其进行序列化.但这不会节省空间.
我能否以或多或少标准化的方式混合"XML 数据和原始二进制数据?
Can I "mix" XML data and raw binary data in a more-or-less standardized way?
我在考虑两个选择:
有没有办法使用 JAXB 做到这一点?
Is there a way to do this using JAXB?
或者有没有办法获取一些现有的 XML 数据并将二进制数据附加到它,以识别边界?
Or is there a way to take some existing XML data and append binary data to it, in such a way that the the boundary is recognized?
我正在寻找的概念不是被/用于 SOAP 吗?
Isn't the concept I'm looking for somehow used by / for SOAP?
还是在电子邮件标准中使用?(二进制附件的分离)
Or is it used in the email standard? (Separation of binary attachments)
我想要实现的方案:
[meta-info-about-boundary][XML-data][boundary][raw-binary-data]
谢谢!
推荐答案
我遵循了 Blaise Doughan 提出的概念,但没有附件 marshallers:
I followed the concept suggested by Blaise Doughan, but without attachment marshallers:
我让 XmlAdapter 将 byte[] 转换为 URI 引用并返回,而引用指向单独的文件,其中原始数据被存储.然后将 XML 文件和所有二进制文件放入一个 zip 文件中.
I let an XmlAdapter convert a byte[] to a URI-reference and back, while references point to separate files, where raw data is stored. The XML file and all binary files are then put into a zip.
类似于OpenOffice的做法和ODF格式,实际上是一个带有少量XML和二进制文件的zip.
It is similar to the approach of OpenOffice and the ODF format, which in fact is a zip with few XMLs and binary files.
(在示例代码中,没有写入实际的二进制文件,也没有创建 zip.)
import java.net.*; import java.util.*; import javax.xml.bind.annotation.*; import javax.xml.bind.annotation.adapters.*; final class Bindings { static final String SCHEME = "storage"; static final Class<?>[] ALL_CLASSES = new Class<?>[]{ Root.class, RawRef.class }; static final class RawRepository extends XmlAdapter<URI, byte[]> { final SortedMap<String, byte[]> map = new TreeMap<>(); final String host; private int lastID = 0; RawRepository(String host) { this.host = host; } @Override public byte[] unmarshal(URI o) { if (!SCHEME.equals(o.getScheme())) { throw new Error("scheme is: " + o.getScheme() + ", while expected was: " + SCHEME); } else if (!host.equals(o.getHost())) { throw new Error("host is: " + o.getHost() + ", while expected was: " + host); } String key = o.getPath(); if (!map.containsKey(key)) { throw new Error("key not found: " + key); } byte[] ret = map.get(key); return Arrays.copyOf(ret, ret.length); } @Override public URI marshal(byte[] o) { ++lastID; String key = String.valueOf(lastID); map.put(key, Arrays.copyOf(o, o.length)); try { return new URI(SCHEME, host, "/" + key, null); } catch (URISyntaxException ex) { throw new Error(ex); } } } @XmlRootElement @XmlType static final class Root { @XmlElement final List<RawRef> element = new LinkedList<>(); } @XmlType static final class RawRef { @XmlJavaTypeAdapter(RawRepository.class) @XmlElement byte[] raw = null; } }
Main.java
import java.io.*; import javax.xml.bind.*; public class _Run { public static void main(String[] args) throws Exception { JAXBContext context = JAXBContext.newInstance(Bindings.ALL_CLASSES); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); Unmarshaller unmarshaller = context.createUnmarshaller(); Bindings.RawRepository adapter = new Bindings.RawRepository("myZipVFS"); marshaller.setAdapter(adapter); Bindings.RawRef ta1 = new Bindings.RawRef(); ta1.raw = "THIS IS A STRING".getBytes(); Bindings.RawRef ta2 = new Bindings.RawRef(); ta2.raw = "THIS IS AN OTHER STRING".getBytes(); Bindings.Root root = new Bindings.Root(); root.element.add(ta1); root.element.add(ta2); StringWriter out = new StringWriter(); marshaller.marshal(root, out); System.out.println(out.toString()); } }
输出
<root> <element> <raw>storage://myZipVFS/1</raw> </element> <element> <raw>storage://myZipVFS/2</raw> </element> </root>