teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

3

テキスト修正

2018/01/04 18:51

投稿

jun68ykt
jun68ykt

スコア9058

answer CHANGED
@@ -56,7 +56,8 @@
56
56
  ----
57
57
  **追記**
58
58
 
59
+ 以下のような別解を考えました。
59
- 質問で問われている本題から離れてしまいますが、次のよな別解をした。
60
+ (ただし、質問で問われている本題から離れてしまうと思うので、あくまで参に留めて頂ければと思いす)
60
61
 
61
62
  所与のハッシュ`sample_hash`は、構造的に次のXMLと等価です。
62
63
  ```xml
@@ -134,13 +135,18 @@
134
135
  取ってこれるパーサーを使えば、自分で再帰的にテキストノードを探すメソッドを作らなくても
135
136
  よくなります。
136
137
 
137
- 上記のコードで、`Hash#to_xml` を再帰メソッドとして実装しているので
138
+ 上記のコードで、`Hash#to_xml` を再帰メソッドとして実装しているので
138
-  「結局手間としては同じでは?」とお思いになるかもしれませんが、
139
+ 「結局手間としては同じでは?」とお思いになるかもしれませんが、
139
-  ハッシュをXMLにするメソッドも自分で書かなくても、
140
+ ハッシュをXMLにするメソッドも自分で書かなくても、
140
-  以下のようなgemがあります。
141
+ 以下のようなgemがあります。
141
142
 
142
143
  - ActiveSupport::[CoreExtensions::Array::Conversions#to_xml](http://api.rubyonrails.org/v2.3/classes/ActiveSupport/CoreExtensions/Array/Conversions.html#M000784)
143
144
 
144
145
  - [Gyoku](https://github.com/savonrb/gyoku)
145
146
 
147
+ この別解で、何が言いたいかと言えば、与件を
148
+ 「XMLのテキストノードへのパスを取得する」問題と置き換えれば、
149
+ 自分で再帰メソッドを書く手間をかけなくても欲しい情報が得られるのでは?
150
+ ということでした。
151
+
146
152
  ご参考になれば幸いです。

2

テキスト修正

2018/01/04 18:51

投稿

jun68ykt
jun68ykt

スコア9058

answer CHANGED
@@ -134,4 +134,13 @@
134
134
  取ってこれるパーサーを使えば、自分で再帰的にテキストノードを探すメソッドを作らなくても
135
135
  よくなります。
136
136
 
137
+ ※上記のコードで、`Hash#to_xml` を再帰メソッドとして実装しているので
138
+  「結局手間としては同じでは?」とお思いになるかもしれませんが、
139
+  ハッシュをXMLにするメソッドも自分で書かなくても、
140
+  以下のようなgemがあります。
141
+
142
+ - ActiveSupport::[CoreExtensions::Array::Conversions#to_xml](http://api.rubyonrails.org/v2.3/classes/ActiveSupport/CoreExtensions/Array/Conversions.html#M000784)
143
+
144
+ - [Gyoku](https://github.com/savonrb/gyoku)
145
+
137
146
  ご参考になれば幸いです。

1

別解追加

2018/01/04 18:42

投稿

jun68ykt
jun68ykt

スコア9058

answer CHANGED
@@ -51,4 +51,87 @@
51
51
  nest1-2/nest2-2/nest3-2b: 50
52
52
 
53
53
 
54
- 参考になりましたら幸いです。
54
+ 参考になりましたら幸いです。
55
+
56
+ ----
57
+ **追記**
58
+
59
+ 質問で問われている本題から離れてしまいますが、次のような別解を考えました。
60
+
61
+ 所与のハッシュ`sample_hash`は、構造的に次のXMLと等価です。
62
+ ```xml
63
+ <?xml version="1.0" encoding="UTF-8"?>
64
+ <root>
65
+ <nest1-1>value1-1</nest1-1>
66
+ <nest1-2>
67
+ <nest2-1>
68
+ <nest3-1a>26513</nest3-1a>
69
+ <nest3-1b>3</nest3-1b>
70
+ </nest2-1>
71
+ <nest2-2>
72
+ <nest3-2a>317829</nest3-2a>
73
+ <nest3-2b>50</nest3-2b>
74
+ </nest2-2>
75
+ </nest1-2>
76
+ </root>
77
+ ```
78
+ ただしXMLにするためには、最上位のノードは1つなので、上記では
79
+ 便宜的に `<root>` 要素をトップに追加しています。
80
+
81
+ XMLを扱う問題に置き換えれば、`value1-1` や `26513`は XMLのテキストノードで、
82
+ これらはXPath で `//text()` という指定でまとめて取得できます。
83
+ あとは、テキストノードのそれぞれについて、XPathを逆に求めれば、
84
+ テキストノードとそれに至るXPathの組を得ることができます。
85
+
86
+ この考えで作成したスクリプトが以下です。
87
+
88
+ ```ruby
89
+ # coding: utf-8
90
+
91
+ require 'nokogiri'
92
+
93
+ sample_hash =
94
+ {'nest1-1' => 'value1-1',
95
+ 'nest1-2' =>
96
+ {
97
+ 'nest2-1' => {
98
+ 'nest3-1a' => '26513',
99
+ 'nest3-1b' => '3'
100
+ },
101
+ 'nest2-2' => {
102
+ 'nest3-2a' => '317829',
103
+ 'nest3-2b' => '50'
104
+ }
105
+ }
106
+ }
107
+
108
+ class Hash
109
+ def to_xml
110
+ map do |k, v|
111
+ text = Hash === v ? v.to_xml : v
112
+ '<%s>%s</%s>' % [k, text, k]
113
+ end.join
114
+ end
115
+ end
116
+
117
+ xml ='<?xml version="1.0" encoding="UTF-8"?><root>%s</root>' % [sample_hash.to_xml]
118
+
119
+ Nokogiri::XML(xml).xpath('//text()').each do |item|
120
+ xpath = Nokogiri::CSS.xpath_for item.css_path
121
+ puts '%s=%s' % [xpath[0], item]
122
+ end
123
+ ```
124
+ 上記では、XMLをパース(解析)するために、`nokogiri` を使っており、
125
+ このスクリプトを実行すると以下が表示されます。
126
+
127
+ > //root/nest1-1/child::text()=value1-1
128
+ //root/nest1-2/nest2-1/nest3-1a/child::text()=26513
129
+ //root/nest1-2/nest2-1/nest3-1b/child::text()=3
130
+ //root/nest1-2/nest2-2/nest3-2a/child::text()=317829
131
+ //root/nest1-2/nest2-2/nest3-2b/child::text()=50
132
+
133
+ このように与題をXMLのテキストノードの走査と考えて、`//text()` でテキストノードをまとめて
134
+ 取ってこれるパーサーを使えば、自分で再帰的にテキストノードを探すメソッドを作らなくても
135
+ よくなります。
136
+
137
+ ご参考になれば幸いです。