2011-10-03 5 views
9

Quando stavo cercando di analizzare xml usando il sassofono sulle prese, mi sono imbattuto in uno strano evento. Analizzando ho notato che DataOutputStream aggiunge 2 byte davanti ai miei dati.Perché DataOutputStream.writeUTF() aggiunge altri 2 byte all'inizio?

messaggio inviare via 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> 

messaggio invio utilizzando 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> 

Come si può notare DataOutputStream aggiunge due byte di fronte al messaggio. Quindi il parser sax lancia l'eccezione "org.xml.sax.SAXParseException: il contenuto non è consentito in prolog". Tuttavia, saltando questi 2 byte, il parser sax funziona bene. Additional Ho notato che DataInputStream non è in grado di leggere il messaggio Transformer.

La mia domanda è: perché DataOutputStream aggiunge questi byte e perché non il Transformer?




Per coloro che sono interessati a replicare il problema qui è un codice:

Server utilizzando 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(); 

Server utilizzando 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; 
} 


client utilizzando SAX Parser:

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); 

client utilizzando DataInputStream:

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

Non so se fa la differenza, ma il vostro primo testo XML non hanno il 'encoding = 'utf-8''. –

+0

Inoltre, perché si desidera utilizzare DataInputStream? Non è necessario quando si legge XML. – StaxMan

+0

DataInputStream è stato utilizzato per il test poiché non ero a conoscenza del fatto che utilizza il proprio protocollo. –

risposta

21

L'uscita del DataOutputStream.writeUTF() è un formato personalizzato, destinato ad essere letto da DataInputStream.readUTF().

Le javadocs del metodo writeUTF si sta chiamando esempio:

scrive una stringa nel flusso di output sottostante usando la codifica UTF-8 modificato in modo indipendente dalla macchina.

Innanzitutto, due byte vengono scritti nello stream di output come se il metodo writeShort indichi il numero di byte da seguire. Questo valore è il numero di byte effettivamente scritto, non la lunghezza della stringa. Seguendo la lunghezza, ogni carattere della stringa viene emesso, in sequenza, utilizzando la codifica UTF-8 modificata per il carattere. Se non viene generata alcuna eccezione, il contatore written viene incrementato del numero totale di byte scritti nel flusso di output. Questo sarà almeno due più la lunghezza di str e al massimo due più tre volte la lunghezza di str.

+1

La domanda è quindi perché vengono utilizzati solo due byte per la lunghezza. Sebbene non sia un caso d'uso comune, questo sembra un limite molto arbitrario. – MauganRa

13

Utilizzare sempre lo stesso tipo di flusso durante la lettura e la scrittura di dati.Se si sta alimentando il flusso direttamente in un parser sax, non si dovrebbe utilizzare un DataOutputStream.

Basta usare

BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); 
bos.write(os.getBytes("UTF-8"));