24 return tonumber(s) |
25 return tonumber(s) |
25 end |
26 end |
26 end |
27 end |
27 |
28 |
28 local value_goes = {} |
29 local value_goes = {} |
|
30 |
|
31 local function resolve_schema(schema, root) |
|
32 if type(schema) == "table" and schema["$ref"] and schema["$ref"]:sub(1, 1) == "#" then |
|
33 local referenced = pointer.resolve(root, schema["$ref"]:sub(2)); |
|
34 if referenced ~= nil then |
|
35 return referenced |
|
36 end |
|
37 end |
|
38 return schema |
|
39 end |
29 |
40 |
30 local function unpack_propschema(propschema, propname, current_ns) |
41 local function unpack_propschema(propschema, propname, current_ns) |
31 |
42 |
32 local proptype = "string" |
43 local proptype = "string" |
33 local value_where = propname and "in_text_tag" or "in_text" |
44 local value_where = propname and "in_text_tag" or "in_text" |
122 elseif value_where == "in_text_tag" then |
133 elseif value_where == "in_text_tag" then |
123 return s:get_child_text(name, namespace) |
134 return s:get_child_text(name, namespace) |
124 end |
135 end |
125 end |
136 end |
126 |
137 |
127 function parse_object(schema, s) |
138 function parse_object(schema, s, root) |
128 local out = {} |
139 local out = {} |
|
140 schema = resolve_schema(schema, root) |
129 if type(schema) == "table" and schema.properties then |
141 if type(schema) == "table" and schema.properties then |
130 for prop, propschema in pairs(schema.properties) do |
142 for prop, propschema in pairs(schema.properties) do |
|
143 propschema = resolve_schema(propschema, root) |
131 |
144 |
132 local proptype, value_where, name, namespace, prefix, single_attribute, enums = unpack_propschema(propschema, prop, s.attr.xmlns) |
145 local proptype, value_where, name, namespace, prefix, single_attribute, enums = unpack_propschema(propschema, prop, s.attr.xmlns) |
133 |
146 |
134 if value_where == "in_children" and type(propschema) == "table" then |
147 if value_where == "in_children" and type(propschema) == "table" then |
135 if proptype == "object" then |
148 if proptype == "object" then |
136 local c = s:get_child(name, namespace) |
149 local c = s:get_child(name, namespace) |
137 if c then |
150 if c then |
138 out[prop] = parse_object(propschema, c); |
151 out[prop] = parse_object(propschema, c, root); |
139 end |
152 end |
140 elseif proptype == "array" then |
153 elseif proptype == "array" then |
141 local a = parse_array(propschema, s); |
154 local a = parse_array(propschema, s, root); |
142 if a and a[1] ~= nil then |
155 if a and a[1] ~= nil then |
143 out[prop] = a; |
156 out[prop] = a; |
144 end |
157 end |
145 else |
158 else |
146 error("unreachable") |
159 error("unreachable") |
147 end |
160 end |
148 elseif value_where == "in_wrapper" and type(propschema) == "table" and proptype == "array" then |
161 elseif value_where == "in_wrapper" and type(propschema) == "table" and proptype == "array" then |
149 local wrapper = s:get_child(name, namespace); |
162 local wrapper = s:get_child(name, namespace); |
150 if wrapper then |
163 if wrapper then |
151 out[prop] = parse_array(propschema, wrapper); |
164 out[prop] = parse_array(propschema, wrapper, root); |
152 end |
165 end |
153 else |
166 else |
154 local value = extract_value(s, value_where, proptype, name, namespace, prefix, single_attribute, enums) |
167 local value = extract_value(s, value_where, proptype, name, namespace, prefix, single_attribute, enums) |
155 |
168 |
156 out[prop] = totype(proptype, value) |
169 out[prop] = totype(proptype, value) |
159 end |
172 end |
160 |
173 |
161 return out |
174 return out |
162 end |
175 end |
163 |
176 |
164 function parse_array(schema, s) |
177 function parse_array(schema, s, root) |
165 local itemschema = schema.items; |
178 local itemschema = resolve_schema(schema.items, root); |
166 local proptype, value_where, child_name, namespace, prefix, single_attribute, enums = unpack_propschema(itemschema, nil, s.attr.xmlns) |
179 local proptype, value_where, child_name, namespace, prefix, single_attribute, enums = unpack_propschema(itemschema, nil, s.attr.xmlns) |
167 local attr_name |
180 local attr_name |
168 if value_where == "in_single_attribute" then |
181 if value_where == "in_single_attribute" then |
169 value_where = "in_attribute"; |
182 value_where = "in_attribute"; |
170 attr_name = single_attribute; |
183 attr_name = single_attribute; |
172 local out = {} |
185 local out = {} |
173 |
186 |
174 if proptype == "object" then |
187 if proptype == "object" then |
175 if type(itemschema) == "table" then |
188 if type(itemschema) == "table" then |
176 for c in s:childtags(child_name, namespace) do |
189 for c in s:childtags(child_name, namespace) do |
177 table.insert(out, parse_object(itemschema, c)); |
190 table.insert(out, parse_object(itemschema, c, root)); |
178 end |
191 end |
179 else |
192 else |
180 error("array items must be schema object") |
193 error("array items must be schema object") |
181 end |
194 end |
182 elseif proptype == "array" then |
195 elseif proptype == "array" then |
183 if type(itemschema) == "table" then |
196 if type(itemschema) == "table" then |
184 for c in s:childtags(child_name, namespace) do |
197 for c in s:childtags(child_name, namespace) do |
185 table.insert(out, parse_array(itemschema, c)); |
198 table.insert(out, parse_array(itemschema, c, root)); |
186 end |
199 end |
187 end |
200 end |
188 else |
201 else |
189 for c in s:childtags(child_name, namespace) do |
202 for c in s:childtags(child_name, namespace) do |
190 local value = extract_value(c, value_where, proptype, attr_name or child_name, namespace, prefix, single_attribute, enums) |
203 local value = extract_value(c, value_where, proptype, attr_name or child_name, namespace, prefix, single_attribute, enums) |
195 return out |
208 return out |
196 end |
209 end |
197 |
210 |
198 local function parse(schema, s) |
211 local function parse(schema, s) |
199 if schema.type == "object" then |
212 if schema.type == "object" then |
200 return parse_object(schema, s) |
213 return parse_object(schema, s, schema) |
201 elseif schema.type == "array" then |
214 elseif schema.type == "array" then |
202 return parse_array(schema, s) |
215 return parse_array(schema, s, schema) |
203 else |
216 else |
204 error("top-level scalars unsupported") |
217 error("top-level scalars unsupported") |
205 end |
218 end |
206 end |
219 end |
207 |
220 |
217 end |
230 end |
218 end |
231 end |
219 |
232 |
220 local unparse |
233 local unparse |
221 |
234 |
222 local function unparse_property(out, v, proptype, propschema, value_where, name, namespace, current_ns, prefix, single_attribute) |
235 local function unparse_property(out, v, proptype, propschema, value_where, name, namespace, current_ns, prefix, |
|
236 single_attribute, root) |
|
237 |
223 if value_where == "in_attribute" then |
238 if value_where == "in_attribute" then |
224 local attr = name |
239 local attr = name |
225 if prefix then |
240 if prefix then |
226 attr = prefix .. ":" .. name |
241 attr = prefix .. ":" .. name |
227 elseif namespace and namespace ~= current_ns then |
242 elseif namespace and namespace ~= current_ns then |
252 out:tag(v, propattr):up(); |
267 out:tag(v, propattr):up(); |
253 elseif proptype == "boolean" and v == true then |
268 elseif proptype == "boolean" and v == true then |
254 out:tag(name, propattr):up(); |
269 out:tag(name, propattr):up(); |
255 end |
270 end |
256 elseif proptype == "object" and type(propschema) == "table" and type(v) == "table" then |
271 elseif proptype == "object" and type(propschema) == "table" and type(v) == "table" then |
257 local c = unparse(propschema, v, name, namespace); |
272 local c = unparse(propschema, v, name, namespace, nil, root); |
258 if c then |
273 if c then |
259 out:add_direct_child(c); |
274 out:add_direct_child(c); |
260 end |
275 end |
261 elseif proptype == "array" and type(propschema) == "table" and type(v) == "table" then |
276 elseif proptype == "array" and type(propschema) == "table" and type(v) == "table" then |
262 if value_where == "in_wrapper" then |
277 if value_where == "in_wrapper" then |
263 local c = unparse(propschema, v, name, namespace); |
278 local c = unparse(propschema, v, name, namespace, nil, root); |
264 if c then |
279 if c then |
265 out:add_direct_child(c); |
280 out:add_direct_child(c); |
266 end |
281 end |
267 else |
282 else |
268 unparse(propschema, v, name, namespace, out); |
283 unparse(propschema, v, name, namespace, out, root); |
269 end |
284 end |
270 else |
285 else |
271 out:text_tag(name, toxmlstring(proptype, v), propattr) |
286 out:text_tag(name, toxmlstring(proptype, v), propattr) |
272 end |
287 end |
273 end |
288 end |
274 end |
289 end |
275 |
290 |
276 function unparse(schema, t, current_name, current_ns, ctx) |
291 function unparse(schema, t, current_name, current_ns, ctx, root) |
|
292 |
|
293 if root == nil then |
|
294 root = schema |
|
295 end |
277 |
296 |
278 if schema.xml then |
297 if schema.xml then |
279 if schema.xml.name then |
298 if schema.xml.name then |
280 current_name = schema.xml.name |
299 current_name = schema.xml.name |
281 end |
300 end |
288 local out = ctx or st.stanza(current_name, {xmlns = current_ns}) |
307 local out = ctx or st.stanza(current_name, {xmlns = current_ns}) |
289 |
308 |
290 if schema.type == "object" then |
309 if schema.type == "object" then |
291 |
310 |
292 for prop, propschema in pairs(schema.properties) do |
311 for prop, propschema in pairs(schema.properties) do |
|
312 propschema = resolve_schema(propschema, root) |
293 local v = t[prop] |
313 local v = t[prop] |
294 |
314 |
295 if v ~= nil then |
315 if v ~= nil then |
296 local proptype, value_where, name, namespace, prefix, single_attribute = unpack_propschema(propschema, prop, current_ns) |
316 local proptype, value_where, name, namespace, prefix, single_attribute = unpack_propschema(propschema, prop, current_ns) |
297 unparse_property(out, v, proptype, propschema, value_where, name, namespace, current_ns, prefix, single_attribute) |
317 unparse_property(out, v, proptype, propschema, value_where, name, namespace, current_ns, prefix, single_attribute, root) |
298 end |
318 end |
299 end |
319 end |
300 return out |
320 return out |
301 |
321 |
302 elseif schema.type == "array" then |
322 elseif schema.type == "array" then |
303 local proptype, value_where, name, namespace, prefix, single_attribute = unpack_propschema(schema.items, current_name, current_ns) |
323 local itemschema = resolve_schema(schema.items, root) |
|
324 local proptype, value_where, name, namespace, prefix, single_attribute = unpack_propschema(itemschema, current_name, current_ns) |
304 for _, item in ipairs(t) do |
325 for _, item in ipairs(t) do |
305 unparse_property(out, item, proptype, schema.items, value_where, name, namespace, current_ns, prefix, single_attribute) |
326 unparse_property(out, item, proptype, itemschema, value_where, name, namespace, current_ns, prefix, single_attribute, root) |
306 end |
327 end |
307 return out |
328 return out |
308 end |
329 end |
309 end |
330 end |
310 |
331 |