16

sax over sockets を使用して xml を解析しようとしたときに、奇妙な現象に遭遇しました。分析すると、DataOutputStream がデータの前に 2 バイトを追加することに気付きました。

DataOutputStream によって送信されるメッセージ:

0020  50 18 00 20 0f df 00 00  00 9d 3c 3f 78 6d 6c 20   P.. .... ..<?xml 
0030  76 65 72 73 69 6f 6e 3d  22 31 2e 30 22 3f 3e 3c   version= "1.0"?><
0040  63 6f 6d 70 61 6e 79 3e  3c 73 74 61 66 66 3e 3c   company> <staff><
0050  66 69 72 73 74 6e 61 6d  65 3e 79 6f 6e 67 3c 2f   firstnam e>yong</
0060  66 69 72 73 74 6e 61 6d  65 3e 3c 6c 61 73 74 6e   firstnam e><lastn
0070  61 6d 65 3e 6d 6f 6f 6b  20 6b 69 6d 3c 2f 6c 61   ame>mook  kim</la
0080  73 74 6e 61 6d 65 3e 3c  6e 69 63 6b 6e 61 6d 65   stname>< nickname
0090  3e c2 a7 3c 2f 6e 69 63  6b 6e 61 6d 65 3e 3c 73   >..</nic kname><s
00a0  61 6c 61 72 79 3e 31 30  30 30 30 30 3c 2f 73 61   alary>10 0000</sa
00b0  6c 61 72 79 3e 3c 2f 73  74 61 66 66 3e 3c 2f 63   lary></s taff></c
00c0  6f 6d 70 61 6e 79 3e                               ompany>

Transformer を使用したメッセージ送信:

0020  50 18 00 20 b6 b1 00 00  3c 3f 78 6d 6c 20 76 65   P.. .... <?xml ve
0030  72 73 69 6f 6e 3d 22 31  2e 30 22 20 65 6e 63 6f   rsion="1 .0" enco
0040  64 69 6e 67 3d 22 75 74  66 2d 38 22 3f 3e 3c 63   ding="ut f-8"?><c
0050  6f 6d 70 61 6e 79 3e 3c  73 74 61 66 66 3e 3c 66   ompany>< staff><f
0060  69 72 73 74 6e 61 6d 65  3e 79 6f 6e 67 3c 2f 66   irstname >yong</f
0070  69 72 73 74 6e 61 6d 65  3e 3c 6c 61 73 74 6e 61   irstname ><lastna
0080  6d 65 3e 6d 6f 6f 6b 20  6b 69 6d 3c 2f 6c 61 73   me>mook  kim</las
0090  74 6e 61 6d 65 3e 3c 6e  69 63 6b 6e 61 6d 65 3e   tname><n ickname>
00a0  c2 a7 3c 2f 6e 69 63 6b  6e 61 6d 65 3e 3c 73 61   ..</nick name><sa
00b0  6c 61 72 79 3e 31 30 30  30 30 30 3c 2f 73 61 6c   lary>100 000</sal
00c0  61 72 79 3e 3c 2f 73 74  61 66 66 3e 3c 2f 63 6f   ary></st aff></co
00d0  6d 70 61 6e 79 3e                                  mpany>  

お気づきかもしれませんが、DataOutputStream はメッセージの前に 2 バイトを追加します。したがって、sax パーサーは例外「org.xml.sax.SAXParseException: Content is not allowed in prolog.」をスローします。ただし、これらの 2 バイトをスキップすると、sax パーサーは問題なく動作します。さらに、DataInputStream が Transformer メッセージを読み取れないことに気付きました。

私の質問は: DataOutputStream がこれらのバイトを追加するのはなぜですか? Transformer はなぜ追加しないのですか?




問題を再現することに興味がある人のために、いくつかのコードを次に示します。

DataInputStream を使用するサーバー:

String data = "<?xml version=\"1.0\"?><company><staff><firstname>yong</firstname><lastname>mook kim</lastname><nickname>§</nickname><salary>100000</salary></staff></company>";
ServerSocket server = new ServerSocket(60000);
Socket socket = server.accept();
DataOutputStream os = new DataOutputStream(socket.getOutputStream());
os.writeUTF(data);
os.close();
socket.close();

Transformer を使用するサーバー:

ServerSocket server = new ServerSocket(60000);
Socket socket = server.accept();
Document doc = createDocument();
printXML(doc, os);
os.close();
socket.close();

public synchronized static void printXML(Document document, OutputStream stream) throws TransformerException
{
    DOMSource domSource = new DOMSource(document);
    StreamResult streamResult = new StreamResult(stream);
    TransformerFactory tf = TransformerFactory.newInstance();
    Transformer serializer = tf.newTransformer();
    serializer.setOutputProperty(OutputKeys.ENCODING, "utf-8");
    serializer.setOutputProperty(OutputKeys.INDENT, "no");
    serializer.transform(domSource, streamResult);
}

private static Document createDocument() throws ParserConfigurationException
{
    Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
    Element company = document.createElement("company");
    Element staff = document.createElement("staff");
    Element firstname = document.createElement("firstname");
    Element lastname = document.createElement("lastname");
    Element nickname = document.createElement("nickname");
    Element salary = document.createElement("salary");
    Text firstnameText = document.createTextNode("yong");
    Text lastnameText = document.createTextNode("mook kim");
    Text nicknameText = document.createTextNode("§");
    Text salaryText = document.createTextNode("100000");
    document.appendChild(company);
    company.appendChild(staff);
    staff.appendChild(firstname);
    staff.appendChild(lastname);
    staff.appendChild(nickname);
    staff.appendChild(salary);
    firstname.appendChild(firstnameText);
    lastname.appendChild(lastnameText);
    nickname.appendChild(nicknameText);
    salary.appendChild(salaryText);
    return document;
}


SAX パーサーを使用するクライアント:

SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
DefaultHandler handler = new MyHandler();
Socket socket = new Socket("localhost", 60000);
InputSource is = new InputSource(new InputStreamReader(socket.getInputStream()));
is.setEncoding("UTF-8");
//socket.getInputStream().skip(2); // skip over the 2 bytes from the DataInputStream
saxParser.parse(is, handler);

DataInputStream を使用するクライアント:

Socket socket = new Socket("localhost", 60000);
DataInputStream os = new DataInputStream(socket.getInputStream());
while(true) {
    String data = os.readUTF();
    System.out.println("Data: " + data);
}
4

2 に答える 2

23

の出力DataOutputStream.writeUTF()は、 による読み取りを目的としたカスタム形式ですDataInputStream.readUTF()

writeUTF呼び出しているメソッドの Javadoc には次のように書かれています。

マシンに依存しない方法で、変更された UTF-8 エンコーディングを使用して、基になる出力ストリームに文字列を書き込みます。

writeShort最初に、後続のバイト数を指定するメソッドによって行われたかのように、2 バイトが出力ストリームに書き込まれます。この値は、文字列の長さではなく、実際に書き出されたバイト数です。長さに続いて、文字の修正された UTF-8 エンコーディングを使用して、文字列の各文字が順番に出力されます。例外がスローされない場合、カウンターwrittenは、出力ストリームに書き込まれた合計バイト数だけインクリメントされます。これは、少なくとも の 2 プラスの長さ、str最大で の 2 プラス 3 倍の長さになりstrます。

于 2011-10-03T01:07:45.757 に答える
21

データの読み取りと書き込みを行うときは、常に同じタイプのストリームを使用してください。ストリームを sax パーサーに直接フィードする場合は、DataOutputStream を使用しないでください。

使うだけ

BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
bos.write(os.getBytes("UTF-8"));
于 2011-10-03T04:35:06.563 に答える