- PR -

XML出力で数値参照(&#xxxx)に変換される

1
投稿者投稿内容
groovy_inoue
会議室デビュー日: 2005/05/18
投稿数: 9
お住まい・勤務地: 東京都
投稿日時: 2007-04-25 21:58
こんにちは

JAXPでXMLを出力しているのですが、(\\uffe4)が、&#65508で出力されてしまいます。
(\\uffe4)が、そのまま(0xfa55)で出力されることを期待していますが、
いろいろ調べたのですが、行き着きませんでした。

ちなみに、エンコード指定は、Shift_JISです。
あと、内部コードとしてHEXダンプ(\\uffe4)で確認をしたので入力時の文字化けはないと考えています。
今回のシステムは、統一コードとして、Shift_JISが要求されているため、UTF-8にできません。


どなたかご存知の方がいらっしゃれば、ご教授ください。


出力部のコードは以下となります。
コード:
TransformerFactory transFactory = TransformerFactory.newInstance();
Transformer transformer = transFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "Shift_JIS");

DOMSource source = new DOMSource(document);
StringWriter writer = new StringWriter();
transformer.transform(source, new StreamResult(writer));
	
strOutputString = writer.toString();

だっちょ
大ベテラン
会議室デビュー日: 2006/12/05
投稿数: 115
投稿日時: 2007-04-26 16:20
 XMLとしては、¦はコード0xFFE4と同じ扱いとなるので、出力されたXMLの文字参照を元に戻すプログラムを作成するほうが現実的です。
 少し難しいので、作っておきました。
Result result = new StreamResult(new File(outfile));

Result result = new StreamResult(new UnentityWriter(new FileOutputStream(outfile)));
にすると、途中に0xFFE4が出力されます。
コード:

/**
* 文字参照型を元のデータに変更して出力
*/
public class UnentityWriter extends OutputStreamWriter {
private boolean m_entity; // 文字参照解析中
private StringBuilder m_entityBuffer;
private OutputStream m_ous; // 元の出力

public UnentityWriter(OutputStream ous)
{
super(ous);
m_ous = ous;
m_entityBuffer = new StringBuilder();
}
public void write(int b) throws IOException
{
if (m_entity) {
if (b==';') {
// 文字参照確定
m_entity = false;
// 文字参照を元に戻して出力
m_entityBuffer.append(';');
byte[] array = unentitySJIS(m_entityBuffer.toString());

m_entityBuffer = new StringBuilder();
super.flush();
m_ous.write(array);
m_ous.flush();
}
else m_entityBuffer.append(b);
}
else {
if (b=='&') {
// 参照開始
m_entity = true;
m_entityBuffer = new StringBuilder();
m_entityBuffer.append('&');
}
else {
super.write(b);
}
}
}
public void write(char[] c, int off, int len) throws IOException
{
// 面倒なのでStringで呼び出す
write(new String(c, off, len));
}
public void write(String str, int off, int len) throws IOException
{
if (m_entity) {
// &直後の場合は#かを判定
if (m_entityBuffer.length()==1) {
if (str.charAt(off)!='#') {
// 実体参照はそのまま出力
super.write('&');
m_entity = false;
}
}
}
if (m_entity) {
// 文字参照中なら;を探す。
for (int i=0;i<len;i++) {
if (str.charAt(off+i)==';') {
// 文字参照確定
m_entity = false;
m_entityBuffer.append(str, off, i);
off += i+1;
len -= i+1;
// 文字参照を元に戻して出力
byte[] array = unentitySJIS(m_entityBuffer.toString());
super.flush();
m_entityBuffer = new StringBuilder();
m_ous.write(array);
m_ous.flush();
}
else m_entityBuffer.append(str.charAt(off+i));
}
}
// ;が見つからなかった場合はbufferにいれたまま次を待つ
if (m_entity) return;

// 文字参照を抜けている場合
for (int i=0;i<len;i++) {
char c = str.charAt(off+i);
if (c=='&') {
// 参照開始
m_entity = true;
m_entityBuffer.append('&');
// 面倒なので再帰呼び出しにする.
// StackOverflowになるほど&が出現することは想定してない
write(str, off+i+1, len-i-1);
return;
}
else {
super.write(c);
}
}
}

/**
* 参照型の文字列が文字参照なら元に戻しSJIS文字として出力.
* @param entity &#XXX;の文字列
*/
private final byte[] unentitySJIS(String entity) throws IOException
{
try {
if (entity.charAt(2)=='x') {
try {
int code = Integer.parseInt(entity.substring(3, entity.length()-1), 16);
// 元のデータにして返す.WindowsなのでLittle endian
byte[] retval = {(byte)(code & 0xFF), (byte)((code >> 8 ) & 0xFF)};
return(retval);
}
catch (Exception e) {
// 文字参照でないのでSJIS文字列にして返す
return(entity.getBytes("SJIS"));
}
}
else {
try {
int code = Integer.parseInt(entity.substring(2, entity.length()-1));
// 元の文字にして返す
// 元のデータにして返す.WindowsなのでLittle endian
byte[] retval = {(byte)(code & 0xFF), (byte)((code >> 8 ) & 0xFF)};
return(retval);
}
catch (Exception e) {
// 文字参照でないのでSJIS文字列にして返す
return(entity.getBytes("SJIS"));
}
}
}
catch (UnsupportedEncodingException ue) {
throw new IOException(ue);
}
}
public void flush() throws IOException
{
// 参照途中の文字列も確定
String s = m_entityBuffer.toString();
super.write(s, 0, s.length());
super.flush();
}
}





[ メッセージ編集済み 編集者: だっちょ 編集日時 2007-04-26 17:53 ]
groovy_inoue
会議室デビュー日: 2005/05/18
投稿数: 9
お住まい・勤務地: 東京都
投稿日時: 2007-04-26 18:46
回答ありがとうございます。

変換後の手動変換は最悪の手立てとして考えていました。

あと、以下の手立て(これも事後変換)でも可能でしたが、問題でしょうか?
@現在、Shift_JIS指定しているところをUTF-8にする。(UTF-8で出力、この時点では当然化けませんが、Unicodeとなってしまいます。)
Aそこで、出力String全体を、Cp943Cで再変換。(IBM WASのため)
B最後に、<?xml version="1.0" encoding="UTF-8"?>を<?xml version="1.0" encoding="Shift_JIS"?>に変換する。


いかがでしょうか?


コード:
TransformerFactory transFactory = TransformerFactory.newInstance();
Transformer transformer = transFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
	
DOMSource source = new DOMSource(document);
StringWriter writer = new StringWriter();
transformer.transform(source, new StreamResult(writer));
	
strOutputString = new String((writer.toString()).getBytes(),"Cp943C");

// ここに<?xml version="1.0" encoding="Shift_JIS"?>に変換する処理を記載。



だっちょ
大ベテラン
会議室デビュー日: 2006/12/05
投稿数: 115
投稿日時: 2007-04-26 19:22
Unicodeで0xFFE4となる文字が、Shift_JISに変換されるときに0xFFE4でなくなると思いますが。
groovy_inoue
会議室デビュー日: 2005/05/18
投稿数: 9
お住まい・勤務地: 東京都
投稿日時: 2007-04-26 20:32
すみません。

言い忘れたのですが、DOMにセットする値を事前にCP943C→MS932の機種依存文字変換を実施しています。

ですので、
コード:
strOutputString = new String((writer.toString()).getBytes(),"Cp943C");


の後に、HEXダンプすると、0xFFE4になっており、最終的にファイル出力で(0xfa55)のバイトコード(HEXエディタ)を確認できました。
groovy_inoue
会議室デビュー日: 2005/05/18
投稿数: 9
お住まい・勤務地: 東京都
投稿日時: 2007-05-08 10:14
結局のところ、いい解決策が見つからず、現段階での最善策として
Shift_JISでのエンコードはやめ、UTF-8でのデフォルトエンコード
でXMLを生成し、XML宣言部のUTF-8を無理やりShift_JISに文字置換
を実施するにいたりました。

今回のシステムでは、他システム連携にMQ(IBMの製品)を使用
しているため、UTF-8で生成した文字列をMQへキューイングする
し、送信するときに、エンコード(Shift_JIS)されるため、アプ
リケーション側ではあくまでUnicodeのままとすることに収まりま
した。

回答いただいた方、どうもありがとうございました。


1

スキルアップ/キャリアアップ(JOB@IT)