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 件のコメント:
コメントを投稿