public static StringDictionary
GetNodeNamespaces(XmlNode node, string defaultNSSubstitute)
{
// 名前空間用辞書オブジェクトの生成
StringDictionary namespaces = new StringDictionary();
// 対象XMLノードからXML属性コレクションを取得
XmlAttributeCollection attribs = node.Attributes;
// XML属性すべてについて
foreach (XmlAttribute attrib in attribs)
{
// "xmlns"で始まる場合
if (attrib.Name.StartsWith("xmlns"))
{
string prefix = null;
// デフォルトの名前空間の場合
if (attrib.Name == "xmlns")
{
// 代替が指定されていれば代替文字列をプリフィクスにして登録
if (!string.IsNullOrEmpty(defaultNSSubstitute))
prefix = defaultNSSubstitute;
else
prefix = string.Empty;
}
// デフォルト以外の場合
else if (attrib.Name[5] == ':' && attrib.Name.Length > 6)
prefix = attrib.Name.Substring(6);
// 名前空間用辞書にプリフィクスと名前空間のペアを追加
if (prefix != null)
{
namespaces.Add(prefix, attrib.Value);
}
}
}
return namespaces;
}
昔書いたこのコードを使いまわそうとして、多少書き直しをしておこうと思い、この辺を直してみようと思いました。
- StringDictionaryをDictinaory<string, string>に。
- varを使う。
- foreach+ifのブロックを、Whereを使ってネストを一つ減らす。
DOMのコレクション類は、IEnumerable<T>の実装ではないですが、IEnumerableは実装しているので、拡張メソッドCast<T>()を使えばWhere拡張メソッドも使えます。
public static Dictionary<string, string>
GetNodeNamespaces(XmlNode node, string defaultNSSubstitute)
{
var namespaces = new Dictionary<string string>();
foreach (var attrib in node.Attributes.Cast<XmlAttribute>()
.Where(a => a.Name.StartsWith("xmlns")))
{
string prefix = null;
if (attrib.Name == "xmlns")
{
if (!string.IsNullOrEmpty(defaultNSSubstitute))
prefix = defaultNSSubstitute;
else
prefix = string.Empty;
}
else if (attrib.Name[5] == ':' && attrib.Name.Length > 6)
prefix = attrib.Name.Substring(6);
if (prefix != null)
{
namespaces.Add(prefix, attrib.Value);
}
}
return namespaces;
}
foreachループの中で、デフォルト名前空間のプレフィックスを決定していますが、よく考えたらforeachの外でも処理できます。さらに、せっかくなのでNULL合体演算子を使いましょう。
public static Dictionary<string, string>
GetNodeNamespaces(XmlNode node, string defaultNSSubstitute)
{
var defprefix = defaultNSSubstitute ?? string.Empty;
var namespaces = new Dictionary<string, string>();
foreach (var attrib in node.Attributes.Cast<XmlAttribute>()
.Where(a => a.Name.StartsWith("xmlns")))
{
string prefix = null;
if (attrib.Name == "xmlns")
prefix = defprefix;
else if (attrib.Name[5] == ':' && attrib.Name.Length > 6)
prefix = attrib.Name.Substring(6);
if (prefix != null)
{
namespaces.Add(prefix, attrib.Value);
}
}
return namespaces;
}
んー。なんかあと一歩な感じがする。もうちょっと考えてみると、foreachループの中で、辞書に突っ込むケースと突っ込まないケースがある。これをそもそもWhereで辞書に突っ込まないケースはフィルタしてしまえばよいかもしれない。
辞書に突っ込むケースというのは、XML属性名が「xmlns」か、「xmlns:」で始まる場合のいずれかのみ。
public static Dictionary<string, string>
GetNodeNamespaces(XmlNode node, string defaultNSSubstitute)
{
var defprefix = defaultNSSubstitute ?? string.Empty;
var namespaces = new Dictionary<string, string>();
foreach (var attrib in node.Attributes.Cast<XmlAttribute>()
.Where(a => a.Name == "xmlns" || a.Name.StartsWith("xmlns:")))
{
string prefix = null;
if (attrib.Name == "xmlns")
prefix = defprefix;
else
prefix = attrib.Name.Substring(6);
namespaces.Add(prefix, attrib.Value);
}
return namespaces;
}
これなら三項演算子を使って、変数をインライン化してしまえば、
public static Dictionary<string, string>
GetNodeNamespaces(XmlNode node, string defaultNSSubstitute)
{
var defprefix = defaultNSSubstitute ?? string.Empty;
var namespaces = new Dictionary<string, string>();
foreach (var attrib in node.Attributes.Cast<XmlAttribute>()
.Where(a => a.Name == "xmlns" || a.Name.StartsWith("xmlns:")))
{
namespaces.Add(attrib.Name == "xmlns" ?
defprefix : attrib.Name.Substring(6), attrib.Value);
}
return namespaces;
}
こうなると、IEnumerable<T>をDictionaryに変換しているだけなので、public static Dictionary<string string>
GetNodeNamespaces(XmlNode node, string defaultNSSubstitute)
{
var defprefix = defaultNSSubstitute ?? string.Empty;
return node.Attributes.Cast<xmlattribute>()
.Where(a => a.Name == "xmlns" || a.Name.StartsWith("xmlns:"))
.ToDictionary(a => a.Name == "xmlns" ?
defprefix : a.Name.Substring(6), a => a.Value);
}
これで済んじゃうなぁ…。最初のコードとの違いに我ながら笑えました。
0 件のコメント:
コメントを投稿