はいそれでは表題の件やっていきます。
C++ GUI Programming with Qt4 348ページにはDOMを使った書き込みのほかにもう一つ方法があると書かれています。
We can generate XML by hand.
(テメェで書け)
だそうです。サンプルもQTextStreamを使ってそれはもうそのまんまXML書いていっているので、なんていうか...見るからにそのまんまなんでここで取り上げるのはやめます。(意味がないので)
なので以上です...というわけではありません。
C++ GUI Programming with Qt4にはさらに
http://doc.trolltech.com/qq/qq05-generating-xml.htmlにXML書き出しのQt3用のシンプルなクラス公開されていると書かれているので、それを今回はQt4用に変換して書いていこうと思います。
いつものようにQtCreaterの使用を前提とします。(QtCreaterなどの使い方は ”Qtをはじめよう" を見てください。)
なお、.proファイルに Qt += xml の記述をしておいてください。でなければQXml関連をコードに含められません。
ではコードを
(xmlwriter.h)
#include <qmap.h>
#include <qstring.h>
#include <qtextstream.h>
#include <qtextcodec.h>
class AttrMap : public QMap<QString, QString>
{
public:
AttrMap() { }
AttrMap( const QString& name, const QString& value ) {
insert( name, value );
}
};
class XmlWriter
{
public:
XmlWriter( QIODevice *device, QTextCodec *codec = 0 );
~XmlWriter();
void writeRaw( const QString& xml );
void writeString( const QString& string );
void writeOpenTag( const QString& name, const AttrMap& attrs = AttrMap() );
void writeCloseTag( const QString& name );
void writeAtomTag( const QString& name, const AttrMap& attrs = AttrMap() );
void writeTaggedString( const QString& name, const QString& string,
const AttrMap& attrs = AttrMap() );
void newLine();
void setIndentSize( int size ) { indentSize = size; }
void setAutoNewLine( bool on ) { autoNewLine = on; }
private:
QString protect( const QString& string );
QString opening( const QString& tag, const AttrMap& attrs = AttrMap() );
void writePendingIndent();
QTextStream out;
QString indentStr;
int indentSize;
bool autoNewLine;
bool atBeginningOfLine;
};
(xmlwriter.cpp)
#include "xmlwriter.h"
XmlWriter::XmlWriter( QIODevice *device, QTextCodec *codec )
: indentSize( 4 ), autoNewLine( false ), atBeginningOfLine( true )
{
out.setDevice( device );
if ( codec == 0 ) {
out.setCodec(QTextCodec::codecForName("UTF-8"));
} else {
out.setCodec( codec );
out << "<?xml version=\"1.0\" encoding=\""
<< protect( codec->name() ) << "\"?>\n";
}
}
XmlWriter::~XmlWriter()
{
if ( autoNewLine && !atBeginningOfLine )
out << endl;
}
QString XmlWriter::protect( const QString& string )
{
QString s = string;
s.replace( "&", "&" );
s.replace( ">", ">" );
s.replace( "<", "<" );
s.replace( "\"", """ );
s.replace( "\'", "'" );
return s;
}
QString XmlWriter::opening( const QString& tag, const AttrMap& attrs )
{
QString s = "<" + tag;
AttrMap::ConstIterator a = attrs.begin();
while ( a != attrs.end() ) {
s += " " + a.key() + "=\"" + protect( *a ) + "\"";
++a;
}
s += ">";
return s;
}
void XmlWriter::writePendingIndent()
{
if ( atBeginningOfLine ) {
out << indentStr;
atBeginningOfLine = false;
}
}
void XmlWriter::newLine()
{
out << endl;
atBeginningOfLine = true;
}
void XmlWriter::writeRaw( const QString& xml )
{
out << xml;
atBeginningOfLine = false;
}
void XmlWriter::writeString( const QString& string )
{
out << protect( string );
atBeginningOfLine = false;
}
void XmlWriter::writeOpenTag( const QString& name, const AttrMap& attrs )
{
writePendingIndent();
out << opening( name, attrs );
indentStr += QString().fill( ' ', indentSize );
if ( autoNewLine )
newLine();
}
void XmlWriter::writeCloseTag( const QString& name )
{
indentStr = indentStr.mid( indentSize );
writePendingIndent();
out << opening( "/" + name );
if ( autoNewLine )
newLine();
}
void XmlWriter::writeAtomTag( const QString& name, const AttrMap& attrs )
{
writePendingIndent();
QString atom = opening( name, attrs );
atom.insert( atom.length() - 1, "/" );
out << atom;
if ( autoNewLine )
newLine();
}
void XmlWriter::writeTaggedString( const QString& name, const QString& string,
const AttrMap& attrs )
{
writePendingIndent();
out << opening( name, attrs );
writeString( string );
out << opening( "/" + name );
if ( autoNewLine )
newLine();
}
はいややこしいですね。めんどくさいのでサラッとしか説明しません。内部で何やってるか詳細に知りたい方は自分でヘッダ、cppを作成してこのコードをコピーしてそれぞれ動作を追ってください。基本UTF-8、第2引数が指定された場合のみそのコーデックとなります。このクラスを使えば変換必須の文字(<とか)などを自動で変換し、またインデントなども処理してくれるので、全部自分で書くよりは楽ですね。
実行コードは以下のようになります。
(mainwindow.cpp)
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "xmlwriter.h"
#include <QTextCodec>
void MainWindow::writeProperty( XmlWriter& xw, const QString& name, const QString& type,
const QString& value )
{
xw.writeOpenTag( "property", AttrMap("name", name) );
xw.writeTaggedString( type, value );
xw.writeCloseTag( "property" );
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QFile file("C:\\Users\\windows7\\Desktop\\test.txt");
if (file.open(QFile::WriteOnly| QFile::Truncate))
{
QTextCodec::setCodecForTr( QTextCodec::codecForName("Shift-JIS"));
XmlWriter xw( &file, QTextCodec::codecForName("Shift-JIS"));
xw.setAutoNewLine( true );
xw.writeRaw( "<!DOCTYPE UI><UI version=\"3.1\">" );
xw.newLine();
xw.writeTaggedString( "class", tr("サンプル1") );
xw.writeOpenTag( tr("サンプルクラス"), AttrMap("class",tr( "サンプル1")) );
writeProperty( xw, "name", "string", tr("名前") );
writeProperty( xw, "caption", "string", tr("キャプション") );
xw.writeOpenTag( "element1-2" );
writeProperty( xw, "name", "string", "ElementName" );
writeProperty( xw, "text", "string", "Rock && Roll" );
xw.writeCloseTag( "element1-2" );
xw.writeCloseTag( tr("サンプルクラス") );
AttrMap attrs;
attrs.insert( "spacing", "6" );
attrs.insert( "margin", "11" );
xw.writeAtomTag( "layoutdefaults", attrs );
xw.writeRaw( "</UI>" );
}
file.close();
}
MainWindow::~MainWindow()
{
delete ui;
}
はい微妙ですね。まぁXMLを全て自分で書くよりはマシになってますが、手間はDOMとあまり変わらないような感じがします。これを実行して書き出されるXMLは以下の通りです。
(書き出されるXML)
<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE UI><UI version="3.1">
<class>サンプル1</class>
<サンプルクラス class="サンプル1">
<property name="name">
<string>名前</string>
</property>
<property name="caption">
<string>キャプション</string>
</property>
<element1-2>
<property name="name">
<string>ElementName</string>
</property>
<property name="text">
<string>Rock && Roll</string>
</property>
</element1-2>
</サンプルクラス>
<layoutdefaults margin="11" spacing="6"/>
</UI>
以上です。
