VB6.0でDOMを使用してXML編集をしていますが、XMLに「<!DOCTYPE cXML SYSTEM "http://xml.・・・」の宣言がない場合は、正常に処理されるのですが、宣言がある場合、「Set xmlnodNode = xmlnodNodeList(0).Attributes.・・・」でエラーとなってしまいます。
ただデバッグ実行した際は、エラーが発生しませんでした。なぜなのでしょう?
エラーを回避する方法を教えてください。
<エラー内容>
オートメーション エラーです。
この操作を完了するのに必要なデータは、まだ利用できません。
【SAMPLE.xml】
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE cXML SYSTEM "http://xml.cxml.org/schemas/cXML/1.2.007/cXML.dtd">
<cXML timestamp="">
<登場人物>
<名前>孫悟空</名前>
</登場人物>
<登場人物>
<名前>牛魔王</名前>
</登場人物>
</cXML>
【VisualBasic】
'XML読込
If Not xmlDocument.Load("SAMPLE.xml") Then
MsgBox "XMLファイルの読込に失敗しました。", vbCritical
Exit Function
End If
Set xmlelmRootElement = xmlDocument.documentElement
Set xmlnodNodeList = xmlDocument.getElementsByTagName("cXML")
Set xmlnodNode = xmlnodNodeList(0).Attributes.getNamedItem("timestamp") '←ここでエラーになる
xmlnodNode.Text = now
■1■ parseError プロパティの内容をチェックしていますか?
→ parseError の errorCode プロパティや reason プロパティには、
解決のために役立つ情報が含まれていることがありますので、
load メソッド失敗時には、これらをチェックしておくと良いでしょう。
← 特に、非同期モードで運用している場合は、load 直後は正常でも、
後から、このプロパティの内容がエラー状態に変化することもあります。
■2■ parsed プロパティが、False の状態になっていませんか?
→外部 DTD のロードには、ダウンロードするだけの遅延が発生します。
初期状態では、非同期モード(async = False)ですので、そのままだと
load 自体は成功しても、後から parseError となる可能性があります。
←XML 自体はローカルにあったとしても、async プロパティは True にして
同期モードを利用すべきでしょう。
# もし非同期モードを利用するのなら、onreadystate プロパティで
# イベント通知を受け取る必要がありますが、VB6 からの利用は困難です。
# (非同期モードは、VBScript や JScript 向けに用意されている機能なので)
■3■ そもそも、XML 自体が不正です。
→ XMLの文法自体は正しいので、XML 文書としては正常な物となっていますが、
DTD 側の定義とは適合しない内容となっているようです。
すなわち、「整形式(well-formed)」の XML 文書とはなっているが、
「妥当(valid)」な XML 文書ではない状態です。
←スキーマを用いず、形式適合検査だけで済ませるのであれば、load 前に
resolveExternals プロパティと validateOnParse を False にしましょう。
逆に、構造厳密検査を必要とするのであれば、それらを True にしておき、
XML ファイルの内容自体も、DTD で定義された XML データに修正しましょう。
■4■ 使用している MSXML のバージョンは幾つですか?
→ MSXML のバージョンによっては、外部 DTD の読み込みに関して、
若干の相違があります。内容次第では、MSXML4 では OK だったのに、
MSXML6 で同じ処理を行うと失敗すること(あるいはその逆)もありますので、
質問時には、MSXML のバージョンや、New (あるいは CreateObject) 部の
コード等も省略せずに提示した方が良いかと思いますよ。
■5■ データ型を一致させましょう。
→ テストコードだからとは思いますが、「xmlnodNode.Text = now」は、
代入先が String 型、右辺が Date 型と不一致です。型を合わせましょう。
← 今回の timestamp 属性は datetime.tz なデータを要求するでしょうから、
xmlnodNode.text = "2007-01-10T13:51:11.012+09:00"
のような文字列代入が必要です。もしくは、nodeTypedValue プロパティから
xmlnodNode.dataType = "datetime.tz"
xmlnodNode.nodeTypedValue = Now()
などとするのも良いかと思います。
訂正。m(_ _)m
> # もし非同期モードを利用するのなら、onreadystate プロパティで
> # イベント通知を受け取る必要がありますが、VB6 からの利用は困難です。
onreadystate プロパティの利用は困難ですが、
onreadystate イベントであれば、下記にて利用できました。
Option Explicit
Private WithEvents xmldoc As MSXML2.DOMDocument
Private Sub xmldoc_onreadystatechange()
If xmldoc.readyState = 4 Then
Rem 解析完了
End If
End Sub
---------------
# http://www.microsoft.com/japan/msdn/columns/askgui/askgui05012001.aspx
# を見て、VB6 では非同期モードを利用できないと思い込んでいました…。
回答ありがとうございました。
「外部DTDのロードには、ダウンロードするだけの遅延が発生する」ということを教えていただいたので以下の対応でエラーを回避するようにしました。
Do
Set xmlelmRootElement = xmlDocument.documentElement
DoEvents
Loop Until Not xmlelmRootElement Is Nothing
>> 初期状態では、非同期モード(async = False)ですので、そのままだと
逆ですね。すみません。
『初期状態では、非同期モード(async = True)』と読み替えてください。
> 以下の対応でエラーを回避するようにしました。
う〜ん、確かにそれでも回避はできるかも知れませんが……。
非同期モードで読み込み、解析完了までループで待機させるぐらいなら、
最初から、同期モードで読み込ませた方がスマートなのでは。
xmlDocument.async = False
If Not xmlDocument.Load("SAMPLE.xml") Then
で、もしも非同期モードによる処理を望むのだとしても、やはり本来は
DoEvents ループではなく、そこは VB らしく「イベント」で処理すべきでしょう。
(そのための方法は、先述したとおりです)
どうしても非同期からの DoEvents ループを行う必要があるのだとしても、
ループ中で documentElement が取得できるかどうかを確認する形よりは、
まずは parsed プロパティ & readyState プロパティの状態をチェックし、
それから documentElement を得る形の方が良い気がします。
以上、蛇足までに。
ツイート | ![]() |