我有以下问题:我的程序传递了一个InputStream,我无法控制其内容。我使用javax库解组我的输入流,如果InputStream包含&,则会正确地抛出异常。字符后面没有“amp;”
我想出的解决方法是创建以下类:
import java.io.ByteArrayInputStream;
import java.io.FilterInputStream;
import java.io.InputStream;
/**
* Provide an input stream where all & characters are properly encoded as &
*/
public class FormattedStream extends FilterInputStream {
public FormattedStream(InputStream src) {
super(new ByteArrayInputStream(StringUtil.toString(src)
.replace("&", "&").replace("amp;amp;", "amp;").getBytes()));
}
}
注意:StringUtil是一个简单的实用程序,我必须将输入流转换为String。
有了这个类,我现在用:
调用JAXB unmarshallerunmarshal(new FormattedStream(inputStream));
而不是
unmarshal(inputStream);
这种方法有效,但由于以下几个原因看起来很奇怪:
1 - 由于super必须是构造函数中的第一个元素的限制(尽管我读到了它,但我无法理解限制),我被迫在一行中完成所有处理,使代码远来自可读。
2 - 将整个流转换为字符串并返回到流似乎过度
3 - 上面的代码略微不正确,因为包含amp; amp; amp;将被修改为包含amp;
我可以通过一个方法提供一个FormatInputStream类来解决1:
InputStream preProcess(InputStream inputStream)
我将在FormattedStream类的构造函数中执行相同的操作,但由于编码限制,必须选择不同的接口似乎很奇怪。
我可以通过保持FormattedStream构造函数简单来解决2:
super(src)
并覆盖三种读取方法,但这将涉及更多编码:通过替换& amp;来覆盖三种读取方法。与我目前拥有的可以利用replaceAll String方法的一行代码相比,动态并不是微不足道的。
至于3,看起来我不担心它的角落情况,但也许我应该......
有关如何以更优雅的方式解决我的问题的任何建议吗?
答案 0 :(得分:3)
我同意McDowell的回答,最重要的是首先修复无效数据源。
无论如何,这是一个InputStream
,它会查找孤独的&
个字符,如果缺少amp;
,则会与其他&
结婚。同样,以这种方式修复损坏的数据并不能在大部分时间内得到回报。
此解决方案修复了OP中提到的三个缺陷,并且只显示了实现转换InputStream的一种方法。
amp;
是否后跟&
所需的四个字节。amp;amp;
,不会以任何方式尝试清除import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.Deque;
public class ReplacerInputStream extends InputStream {
private static final byte[] REPLACEMENT = "amp;".getBytes();
private final byte[] readBuf = new byte[REPLACEMENT.length];
private final Deque<Byte> backBuf = new ArrayDeque<Byte>();
private final InputStream in;
public ReplacerInputStream(InputStream in) {
this.in = in;
}
@Override
public int read() throws IOException {
if (!backBuf.isEmpty()) {
return backBuf.pop();
}
int first = in.read();
if (first == '&') {
peekAndReplace();
}
return first;
}
private void peekAndReplace() throws IOException {
int read = super.read(readBuf, 0, REPLACEMENT.length);
for (int i1 = read - 1; i1 >= 0; i1--) {
backBuf.push(readBuf[i1]);
}
for (int i = 0; i < REPLACEMENT.length; i++) {
if (read != REPLACEMENT.length || readBuf[i] != REPLACEMENT[i]) {
for (int j = REPLACEMENT.length - 1; j >= 0; j--) {
// In reverse order
backBuf.push(REPLACEMENT[j]);
}
return;
}
}
}
}
,因为这种解决方案不会发生这种情况。
test("Foo & Bar", "Foo & Bar");
test("&&&", "&&&");
test("&&& ", "&&& ");
test(" &&&", " &&&");
test("&", "&");
test("&", "&");
test("&&", "&&");
test("&&&", "&&&");
test("test", "test");
test("", "");
test("testtesttest&", "testtesttest&");
已使用以下输入数据测试代码(第一个参数是预期输出,第二个参数是原始输入):
{{1}}
答案 1 :(得分:0)
为了避免将所有数据读入RAM,您可以实现FilterInputStream
(您必须覆盖read()
和read(byte[],int,int)
并查看以某种方式缓冲这些额外字节。这将不会导致更短的代码。
真正的解决方案是修复无效的数据源(如果您要自动执行该操作,则需要编写自己的XML解析器)。
你的方法有一些缺陷。
String.getBytes()
的结果取决于系统;它也是一种代码转换操作,可能与StringUtil.toString
所做的任何事情都不对称 - 许多系统上的默认编码都是lossy。您应该使用XML document encoding。