ox 1.5.7 → 1.5.8

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of ox might be problematic. Click here for more details.

data/README.md CHANGED
@@ -30,9 +30,9 @@ A fast XML parser and Object marshaller as a Ruby gem.
30
30
 
31
31
  ## <a name="release">Release Notes</a>
32
32
 
33
- ### Release 1.5.7
33
+ ### Release 1.5.8
34
34
 
35
- - Made compatible with OS X 10.6.8 native Ruby 1.9.1.
35
+ - Added an option to not symbolize attribute keys in generic parsing.
36
36
 
37
37
  ## <a name="description">Description</a>
38
38
 
@@ -114,23 +114,34 @@ create_prolog_doc(PInfo pi, const char *target, Attr attrs) {
114
114
  doc = rb_obj_alloc(ox_document_clas);
115
115
  ah = rb_hash_new();
116
116
  for (; 0 != attrs->name; attrs++) {
117
+ if (Yes == pi->options->sym_keys) {
117
118
  #if HAS_ENCODING_SUPPORT
118
- if (0 != pi->encoding) {
119
- VALUE rstr = rb_str_new2(attrs->name);
119
+ if (0 != pi->encoding) {
120
+ VALUE rstr = rb_str_new2(attrs->name);
120
121
 
121
- rb_enc_associate(rstr, pi->encoding);
122
- sym = rb_funcall(rstr, ox_to_sym_id, 0);
123
- } else {
124
- sym = ID2SYM(rb_intern(attrs->name));
125
- }
122
+ rb_enc_associate(rstr, pi->encoding);
123
+ sym = rb_funcall(rstr, ox_to_sym_id, 0);
124
+ } else {
125
+ sym = ID2SYM(rb_intern(attrs->name));
126
+ }
126
127
  #else
127
- sym = ID2SYM(rb_intern(attrs->name));
128
+ sym = ID2SYM(rb_intern(attrs->name));
128
129
  #endif
129
- rb_hash_aset(ah, sym, rb_str_new2(attrs->value));
130
+ rb_hash_aset(ah, sym, rb_str_new2(attrs->value));
131
+ } else {
132
+ VALUE rstr = rb_str_new2(attrs->name);
133
+
130
134
  #if HAS_ENCODING_SUPPORT
131
- if (0 == strcmp("encoding", attrs->name)) {
132
- pi->encoding = rb_enc_find(attrs->value);
133
- }
135
+ if (0 != pi->encoding) {
136
+ rb_enc_associate(rstr, pi->encoding);
137
+ }
138
+ #endif
139
+ rb_hash_aset(ah, rstr, rb_str_new2(attrs->value));
140
+ }
141
+ #if HAS_ENCODING_SUPPORT
142
+ if (0 == strcmp("encoding", attrs->name)) {
143
+ pi->encoding = rb_enc_find(attrs->value);
144
+ }
134
145
  #endif
135
146
  }
136
147
  nodes = rb_ary_new();
@@ -154,7 +165,7 @@ instruct(PInfo pi, const char *target, Attr attrs) {
154
165
  // ignore other instructions
155
166
  }
156
167
  } else {
157
- if (TRACE <= pi->trace) {
168
+ if (TRACE <= pi->options->trace) {
158
169
  printf("Processing instruction %s ignored.\n", target);
159
170
  }
160
171
  }
@@ -187,7 +198,7 @@ nomode_instruct(PInfo pi, const char *target, Attr attrs) {
187
198
  }
188
199
  }
189
200
  } else {
190
- if (TRACE <= pi->trace) {
201
+ if (TRACE <= pi->options->trace) {
191
202
  printf("Processing instruction %s ignored.\n", target);
192
203
  }
193
204
  }
@@ -278,21 +289,30 @@ add_element(PInfo pi, const char *ename, Attr attrs, int hasChildren) {
278
289
  VALUE sym;
279
290
  VALUE *slot;
280
291
 
281
- if (Qundef == (sym = ox_cache_get(ox_symbol_cache, attrs->name, &slot))) {
292
+ if (Yes == pi->options->sym_keys) {
293
+ if (Qundef == (sym = ox_cache_get(ox_symbol_cache, attrs->name, &slot))) {
282
294
  #if HAS_ENCODING_SUPPORT
283
- if (0 != pi->encoding) {
284
- VALUE rstr = rb_str_new2(attrs->name);
295
+ if (0 != pi->encoding) {
296
+ VALUE rstr = rb_str_new2(attrs->name);
285
297
 
286
- rb_enc_associate(rstr, pi->encoding);
287
- sym = rb_funcall(rstr, ox_to_sym_id, 0);
288
- } else {
298
+ rb_enc_associate(rstr, pi->encoding);
299
+ sym = rb_funcall(rstr, ox_to_sym_id, 0);
300
+ } else {
301
+ sym = ID2SYM(rb_intern(attrs->name));
302
+ }
303
+ #else
289
304
  sym = ID2SYM(rb_intern(attrs->name));
305
+ #endif
306
+ *slot = sym;
307
+ }
308
+ } else {
309
+ sym = rb_str_new2(attrs->name);
310
+ #if HAS_ENCODING_SUPPORT
311
+ if (0 != pi->encoding) {
312
+ rb_enc_associate(sym, pi->encoding);
290
313
  }
291
- #else
292
- sym = ID2SYM(rb_intern(attrs->name));
293
314
  #endif
294
- *slot = sym;
295
- }
315
+ }
296
316
  s = rb_str_new2(attrs->value);
297
317
  #if HAS_ENCODING_SUPPORT
298
318
  if (0 != pi->encoding) {
@@ -237,7 +237,7 @@ classname2class(const char *name, PInfo pi, VALUE base_class) {
237
237
  if (':' != *n) {
238
238
  raise_error("Invalid classname, expected another ':'", pi->str, pi->s);
239
239
  }
240
- if (Qundef == (clas = resolve_classname(clas, class_name, pi->effort, base_class))) {
240
+ if (Qundef == (clas = resolve_classname(clas, class_name, pi->options->effort, base_class))) {
241
241
  return Qundef;
242
242
  }
243
243
  s = class_name;
@@ -246,7 +246,7 @@ classname2class(const char *name, PInfo pi, VALUE base_class) {
246
246
  }
247
247
  }
248
248
  *s = '\0';
249
- if (Qundef != (clas = resolve_classname(clas, class_name, pi->effort, base_class))) {
249
+ if (Qundef != (clas = resolve_classname(clas, class_name, pi->options->effort, base_class))) {
250
250
  *slot = clas;
251
251
  }
252
252
  }
@@ -411,7 +411,7 @@ add_text(PInfo pi, char *text, int closed) {
411
411
  if (!closed) {
412
412
  raise_error("Text not closed", pi->str, pi->s);
413
413
  }
414
- if (DEBUG <= pi->trace) {
414
+ if (DEBUG <= pi->options->trace) {
415
415
  char indent[128];
416
416
 
417
417
  fill_indent(pi, indent, sizeof(indent));
@@ -540,7 +540,7 @@ add_element(PInfo pi, const char *ename, Attr attrs, int hasChildren) {
540
540
  Helper h;
541
541
  unsigned long id;
542
542
 
543
- if (TRACE <= pi->trace) {
543
+ if (TRACE <= pi->options->trace) {
544
544
  char buf[1024];
545
545
  char indent[128];
546
546
  char *s = buf;
@@ -552,7 +552,7 @@ add_element(PInfo pi, const char *ename, Attr attrs, int hasChildren) {
552
552
  }
553
553
  *s++ = '>';
554
554
  *s++ = '\0';
555
- if (DEBUG <= pi->trace) {
555
+ if (DEBUG <= pi->options->trace) {
556
556
  debug_stack(pi, buf);
557
557
  } else {
558
558
  fill_indent(pi, indent, sizeof(indent));
@@ -627,7 +627,7 @@ add_element(PInfo pi, const char *ename, Attr attrs, int hasChildren) {
627
627
  break;
628
628
  case RawCode:
629
629
  if (hasChildren) {
630
- h->obj = ox_parse(pi->s, ox_gen_callbacks, &pi->s, pi->trace, pi->effort);
630
+ h->obj = ox_parse(pi->s, ox_gen_callbacks, &pi->s, pi->options);
631
631
  if (0 != pi->circ_array) {
632
632
  circ_array_set(pi->circ_array, h->obj, get_id_from_attrs(pi, attrs));
633
633
  }
@@ -673,17 +673,17 @@ add_element(PInfo pi, const char *ename, Attr attrs, int hasChildren) {
673
673
  raise_error("Invalid element name", pi->str, pi->s);
674
674
  break;
675
675
  }
676
- if (DEBUG <= pi->trace) {
676
+ if (DEBUG <= pi->options->trace) {
677
677
  debug_stack(pi, " -----------");
678
678
  }
679
679
  }
680
680
 
681
681
  static void
682
682
  end_element(PInfo pi, const char *ename) {
683
- if (TRACE <= pi->trace) {
683
+ if (TRACE <= pi->options->trace) {
684
684
  char indent[128];
685
685
 
686
- if (DEBUG <= pi->trace) {
686
+ if (DEBUG <= pi->options->trace) {
687
687
  char buf[1024];
688
688
 
689
689
  snprintf(buf, sizeof(buf) - 1, "</%s>", ename);
@@ -779,7 +779,7 @@ end_element(PInfo pi, const char *ename) {
779
779
  circ_array_free(pi->circ_array);
780
780
  pi->circ_array = 0;
781
781
  }
782
- if (DEBUG <= pi->trace) {
782
+ if (DEBUG <= pi->options->trace) {
783
783
  debug_stack(pi, " ----------");
784
784
  }
785
785
  }
@@ -40,13 +40,13 @@
40
40
  #define SMALL_XML 65536
41
41
 
42
42
  typedef struct _YesNoOpt {
43
- VALUE sym;
44
- char *attr;
43
+ VALUE sym;
44
+ char *attr;
45
45
  } *YesNoOpt;
46
46
 
47
47
  void Init_ox();
48
48
 
49
- VALUE Ox = Qnil;
49
+ VALUE Ox = Qnil;
50
50
 
51
51
  ID ox_at_id;
52
52
  ID ox_at_value_id;
@@ -86,66 +86,68 @@ ID ox_tv_nsec_id;
86
86
  ID ox_tv_usec_id;
87
87
  ID ox_value_id;
88
88
 
89
- VALUE ox_encoding_sym;
89
+ VALUE ox_encoding_sym;
90
90
 
91
- VALUE ox_empty_string;
92
- VALUE ox_zero_fixnum;
91
+ VALUE ox_empty_string;
92
+ VALUE ox_zero_fixnum;
93
93
 
94
- VALUE ox_cdata_clas;
95
- VALUE ox_comment_clas;
96
- VALUE ox_doctype_clas;
97
- VALUE ox_document_clas;
98
- VALUE ox_element_clas;
99
- VALUE ox_bag_clas;
100
- VALUE ox_struct_class;
101
- VALUE ox_time_class;
102
- VALUE ox_date_class;
94
+ VALUE ox_cdata_clas;
95
+ VALUE ox_comment_clas;
96
+ VALUE ox_doctype_clas;
97
+ VALUE ox_document_clas;
98
+ VALUE ox_element_clas;
99
+ VALUE ox_bag_clas;
100
+ VALUE ox_struct_class;
101
+ VALUE ox_time_class;
102
+ VALUE ox_date_class;
103
103
  VALUE ox_stringio_class;
104
104
 
105
- Cache ox_symbol_cache = 0;
106
- Cache ox_class_cache = 0;
107
- Cache ox_attr_cache = 0;
105
+ Cache ox_symbol_cache = 0;
106
+ Cache ox_class_cache = 0;
107
+ Cache ox_attr_cache = 0;
108
108
 
109
- static VALUE xsd_date_sym;
110
- static VALUE opt_format_sym;
111
- static VALUE circular_sym;
112
- static VALUE mode_sym;
109
+ static VALUE auto_define_sym;
113
110
  static VALUE auto_sym;
114
- static VALUE optimized_sym;
115
- static VALUE object_sym;
111
+ static VALUE circular_sym;
112
+ static VALUE convert_special_sym;
113
+ static VALUE effort_sym;
116
114
  static VALUE generic_sym;
115
+ static VALUE indent_sym;
117
116
  static VALUE limited_sym;
117
+ static VALUE mode_sym;
118
+ static VALUE object_sym;
119
+ static VALUE opt_format_sym;
120
+ static VALUE optimized_sym;
121
+ static VALUE strict_sym;
118
122
  static VALUE strict_sym;
123
+ static VALUE symbolize_keys_sym;
119
124
  static VALUE tolerant_sym;
120
- static VALUE effort_sym;
121
- static VALUE auto_define_sym;
122
125
  static VALUE trace_sym;
123
- static VALUE strict_sym;
124
126
  static VALUE with_dtd_sym;
125
127
  static VALUE with_instruct_sym;
126
- static VALUE convert_special_sym;
127
128
  static VALUE with_xml_sym;
128
- static VALUE indent_sym;
129
+ static VALUE xsd_date_sym;
129
130
 
130
- struct _Options ox_default_options = {
131
- { '\0' }, // encoding
132
- 2, // indent
133
- 0, // trace
134
- No, // with_dtd
135
- No, // with_xml
136
- No, // with_instruct
137
- No, // circular
138
- No, // xsd_date
139
- NoMode, // mode
140
- StrictEffort, // effort
131
+ struct _Options ox_default_options = {
132
+ { '\0' }, // encoding
133
+ 2, // indent
134
+ 0, // trace
135
+ No, // with_dtd
136
+ No, // with_xml
137
+ No, // with_instruct
138
+ No, // circular
139
+ No, // xsd_date
140
+ NoMode, // mode
141
+ StrictEffort, // effort
142
+ Yes // sym_keys
141
143
  };
142
144
 
143
- extern ParseCallbacks ox_obj_callbacks;
144
- extern ParseCallbacks ox_gen_callbacks;
145
- extern ParseCallbacks ox_limited_callbacks;
146
- extern ParseCallbacks ox_nomode_callbacks;
145
+ extern ParseCallbacks ox_obj_callbacks;
146
+ extern ParseCallbacks ox_gen_callbacks;
147
+ extern ParseCallbacks ox_limited_callbacks;
148
+ extern ParseCallbacks ox_nomode_callbacks;
147
149
 
148
- static void parse_dump_options(VALUE ropts, Options copts);
150
+ static void parse_dump_options(VALUE ropts, Options copts);
149
151
 
150
152
  /* call-seq: ox_default_options() => Hash
151
153
  *
@@ -160,12 +162,13 @@ static void parse_dump_options(VALUE ropts, Options copts);
160
162
  * - xsd_date: [true|false|nil] use XSD date format instead of decimal format
161
163
  * - mode: [:object|:generic|:limited|nil] load method to use for XML
162
164
  * - effort: [:strict|:tolerant|:auto_define] set the tolerance level for loading
165
+ * - symbolize_keys: [true|false|nil] symbolize element attribute keys or leave as Strings
163
166
  * @return [Hash] all current option settings.
164
167
  */
165
168
  static VALUE
166
169
  get_def_opts(VALUE self) {
167
- VALUE opts = rb_hash_new();
168
- int elen = (int)strlen(ox_default_options.encoding);
170
+ VALUE opts = rb_hash_new();
171
+ int elen = (int)strlen(ox_default_options.encoding);
169
172
 
170
173
  rb_hash_aset(opts, ox_encoding_sym, (0 == elen) ? Qnil : rb_str_new(ox_default_options.encoding, elen));
171
174
  rb_hash_aset(opts, indent_sym, INT2FIX(ox_default_options.indent));
@@ -175,19 +178,20 @@ get_def_opts(VALUE self) {
175
178
  rb_hash_aset(opts, with_instruct_sym, (Yes == ox_default_options.with_instruct) ? Qtrue : ((No == ox_default_options.with_instruct) ? Qfalse : Qnil));
176
179
  rb_hash_aset(opts, circular_sym, (Yes == ox_default_options.circular) ? Qtrue : ((No == ox_default_options.circular) ? Qfalse : Qnil));
177
180
  rb_hash_aset(opts, xsd_date_sym, (Yes == ox_default_options.xsd_date) ? Qtrue : ((No == ox_default_options.xsd_date) ? Qfalse : Qnil));
181
+ rb_hash_aset(opts, symbolize_keys_sym, (Yes == ox_default_options.sym_keys) ? Qtrue : ((No == ox_default_options.sym_keys) ? Qfalse : Qnil));
178
182
  switch (ox_default_options.mode) {
179
- case ObjMode: rb_hash_aset(opts, mode_sym, object_sym); break;
180
- case GenMode: rb_hash_aset(opts, mode_sym, generic_sym); break;
181
- case LimMode: rb_hash_aset(opts, mode_sym, limited_sym); break;
183
+ case ObjMode: rb_hash_aset(opts, mode_sym, object_sym); break;
184
+ case GenMode: rb_hash_aset(opts, mode_sym, generic_sym); break;
185
+ case LimMode: rb_hash_aset(opts, mode_sym, limited_sym); break;
182
186
  case NoMode:
183
- default: rb_hash_aset(opts, mode_sym, Qnil); break;
187
+ default: rb_hash_aset(opts, mode_sym, Qnil); break;
184
188
  }
185
189
  switch (ox_default_options.effort) {
186
- case StrictEffort: rb_hash_aset(opts, effort_sym, strict_sym); break;
187
- case TolerantEffort: rb_hash_aset(opts, effort_sym, tolerant_sym); break;
188
- case AutoEffort: rb_hash_aset(opts, effort_sym, auto_define_sym); break;
190
+ case StrictEffort: rb_hash_aset(opts, effort_sym, strict_sym); break;
191
+ case TolerantEffort: rb_hash_aset(opts, effort_sym, tolerant_sym); break;
192
+ case AutoEffort: rb_hash_aset(opts, effort_sym, auto_define_sym); break;
189
193
  case NoEffort:
190
- default: rb_hash_aset(opts, effort_sym, Qnil); break;
194
+ default: rb_hash_aset(opts, effort_sym, Qnil); break;
191
195
  }
192
196
  return opts;
193
197
  }
@@ -206,79 +210,81 @@ get_def_opts(VALUE self) {
206
210
  * @param [true|false|nil] :xsd_date use XSD date format instead of decimal format
207
211
  * @param [:object|:generic|:limited|nil] :mode load method to use for XML
208
212
  * @param [:strict|:tolerant|:auto_define] :effort set the tolerance level for loading
213
+ * @param [true|false|nil] :symbolize_keys symbolize element attribute keys or leave as Strings
209
214
  * @return [nil]
210
215
  */
211
216
  static VALUE
212
217
  set_def_opts(VALUE self, VALUE opts) {
213
- struct _YesNoOpt ynos[] = {
214
- { with_xml_sym, &ox_default_options.with_xml },
215
- { with_dtd_sym, &ox_default_options.with_dtd },
216
- { with_instruct_sym, &ox_default_options.with_instruct },
217
- { xsd_date_sym, &ox_default_options.xsd_date },
218
- { circular_sym, &ox_default_options.circular },
219
- { Qnil, 0 }
218
+ struct _YesNoOpt ynos[] = {
219
+ { with_xml_sym, &ox_default_options.with_xml },
220
+ { with_dtd_sym, &ox_default_options.with_dtd },
221
+ { with_instruct_sym, &ox_default_options.with_instruct },
222
+ { xsd_date_sym, &ox_default_options.xsd_date },
223
+ { circular_sym, &ox_default_options.circular },
224
+ { symbolize_keys_sym, &ox_default_options.sym_keys },
225
+ { Qnil, 0 }
220
226
  };
221
- YesNoOpt o;
222
- VALUE v;
227
+ YesNoOpt o;
228
+ VALUE v;
223
229
 
224
230
  Check_Type(opts, T_HASH);
225
231
 
226
232
  v = rb_hash_aref(opts, ox_encoding_sym);
227
233
  if (Qnil == v) {
228
- *ox_default_options.encoding = '\0';
234
+ *ox_default_options.encoding = '\0';
229
235
  } else {
230
- Check_Type(v, T_STRING);
231
- strncpy(ox_default_options.encoding, StringValuePtr(v), sizeof(ox_default_options.encoding) - 1);
236
+ Check_Type(v, T_STRING);
237
+ strncpy(ox_default_options.encoding, StringValuePtr(v), sizeof(ox_default_options.encoding) - 1);
232
238
  }
233
239
 
234
240
  v = rb_hash_aref(opts, indent_sym);
235
241
  if (Qnil != v) {
236
- Check_Type(v, T_FIXNUM);
237
- ox_default_options.indent = FIX2INT(v);
242
+ Check_Type(v, T_FIXNUM);
243
+ ox_default_options.indent = FIX2INT(v);
238
244
  }
239
245
 
240
246
  v = rb_hash_aref(opts, trace_sym);
241
247
  if (Qnil != v) {
242
- Check_Type(v, T_FIXNUM);
243
- ox_default_options.trace = FIX2INT(v);
248
+ Check_Type(v, T_FIXNUM);
249
+ ox_default_options.trace = FIX2INT(v);
244
250
  }
245
251
 
246
252
  v = rb_hash_aref(opts, mode_sym);
247
253
  if (Qnil == v) {
248
- ox_default_options.mode = NoMode;
254
+ ox_default_options.mode = NoMode;
249
255
  } else if (object_sym == v) {
250
- ox_default_options.mode = ObjMode;
256
+ ox_default_options.mode = ObjMode;
251
257
  } else if (generic_sym == v) {
252
- ox_default_options.mode = GenMode;
258
+ ox_default_options.mode = GenMode;
253
259
  } else if (limited_sym == v) {
254
- ox_default_options.mode = LimMode;
260
+ ox_default_options.mode = LimMode;
255
261
  } else {
256
- rb_raise(rb_eArgError, ":mode must be :object, :generic, :limited, or nil.\n");
262
+ rb_raise(rb_eArgError, ":mode must be :object, :generic, :limited, or nil.\n");
257
263
  }
258
264
 
259
265
  v = rb_hash_aref(opts, effort_sym);
260
266
  if (Qnil == v) {
261
- ox_default_options.effort = NoEffort;
267
+ ox_default_options.effort = NoEffort;
262
268
  } else if (strict_sym == v) {
263
- ox_default_options.effort = StrictEffort;
269
+ ox_default_options.effort = StrictEffort;
264
270
  } else if (tolerant_sym == v) {
265
- ox_default_options.effort = TolerantEffort;
271
+ ox_default_options.effort = TolerantEffort;
266
272
  } else if (auto_define_sym == v) {
267
- ox_default_options.effort = AutoEffort;
273
+ ox_default_options.effort = AutoEffort;
268
274
  } else {
269
- rb_raise(rb_eArgError, ":effort must be :strict, :tolerant, :auto_define, or nil.\n");
275
+ rb_raise(rb_eArgError, ":effort must be :strict, :tolerant, :auto_define, or nil.\n");
270
276
  }
271
277
  for (o = ynos; 0 != o->attr; o++) {
272
- v = rb_hash_lookup(opts, o->sym);
273
- if (Qnil == v) {
274
- *o->attr = NotSet;
275
- } else if (Qtrue == v) {
276
- *o->attr = Yes;
277
- } else if (Qfalse == v) {
278
- *o->attr = No;
279
- } else {
278
+ v = rb_hash_lookup(opts, o->sym);
279
+ if (Qnil == v) {
280
+ *o->attr = NotSet;
281
+ } else if (Qtrue == v) {
282
+ *o->attr = Yes;
283
+ } else if (Qfalse == v) {
284
+ *o->attr = No;
285
+ } else {
280
286
  rb_raise(rb_eArgError, "%s must be true or false.\n", rb_id2name(SYM2ID(o->sym)));
281
- }
287
+ }
282
288
  }
283
289
  return Qnil;
284
290
  }
@@ -295,7 +301,7 @@ set_def_opts(VALUE self, VALUE opts) {
295
301
  */
296
302
  static VALUE
297
303
  to_obj(VALUE self, VALUE ruby_xml) {
298
- char *xml;
304
+ char *xml;
299
305
  size_t len;
300
306
  VALUE obj;
301
307
 
@@ -308,7 +314,7 @@ to_obj(VALUE self, VALUE ruby_xml) {
308
314
  xml = ALLOCA_N(char, len);
309
315
  }
310
316
  strcpy(xml, StringValuePtr(ruby_xml));
311
- obj = ox_parse(xml, ox_obj_callbacks, 0, 0, StrictEffort);
317
+ obj = ox_parse(xml, ox_obj_callbacks, 0, &ox_default_options);
312
318
  if (SMALL_XML < len) {
313
319
  xfree(xml);
314
320
  }
@@ -324,9 +330,9 @@ to_obj(VALUE self, VALUE ruby_xml) {
324
330
  */
325
331
  static VALUE
326
332
  to_gen(VALUE self, VALUE ruby_xml) {
327
- char *xml;
328
- size_t len;
329
- VALUE obj;
333
+ char *xml;
334
+ size_t len;
335
+ VALUE obj;
330
336
 
331
337
  Check_Type(ruby_xml, T_STRING);
332
338
  // the xml string gets modified so make a copy of it
@@ -337,7 +343,7 @@ to_gen(VALUE self, VALUE ruby_xml) {
337
343
  xml = ALLOCA_N(char, len);
338
344
  }
339
345
  strcpy(xml, StringValuePtr(ruby_xml));
340
- obj = ox_parse(xml, ox_gen_callbacks, 0, 0, StrictEffort);
346
+ obj = ox_parse(xml, ox_gen_callbacks, 0, &ox_default_options);
341
347
  if (SMALL_XML < len) {
342
348
  xfree(xml);
343
349
  }
@@ -346,58 +352,61 @@ to_gen(VALUE self, VALUE ruby_xml) {
346
352
 
347
353
  static VALUE
348
354
  load(char *xml, int argc, VALUE *argv, VALUE self) {
349
- VALUE obj;
350
- struct _Options options = ox_default_options;
355
+ VALUE obj;
356
+ struct _Options options = ox_default_options;
351
357
 
352
358
  if (1 == argc && rb_cHash == rb_obj_class(*argv)) {
353
- VALUE h = *argv;
354
- VALUE v;
355
-
356
- if (Qnil != (v = rb_hash_lookup(h, mode_sym))) {
357
- if (object_sym == v) {
358
- options.mode = ObjMode;
359
- } else if (optimized_sym == v) {
360
- options.mode = ObjMode;
361
- } else if (generic_sym == v) {
362
- options.mode = GenMode;
363
- } else if (limited_sym == v) {
364
- options.mode = LimMode;
365
- } else {
366
- rb_raise(rb_eArgError, ":mode must be :generic, :object, or :limited.\n");
367
- }
368
- }
369
- if (Qnil != (v = rb_hash_lookup(h, effort_sym))) {
370
- if (auto_define_sym == v) {
371
- options.effort = AutoEffort;
372
- } else if (tolerant_sym == v) {
373
- options.effort = TolerantEffort;
374
- } else if (strict_sym == v) {
375
- options.effort = StrictEffort;
376
- } else {
377
- rb_raise(rb_eArgError, ":effort must be :strict, :tolerant, or :auto_define.\n");
378
- }
379
- }
380
- if (Qnil != (v = rb_hash_lookup(h, trace_sym))) {
381
- Check_Type(v, T_FIXNUM);
382
- options.trace = FIX2INT(v);
383
- }
359
+ VALUE h = *argv;
360
+ VALUE v;
361
+
362
+ if (Qnil != (v = rb_hash_lookup(h, mode_sym))) {
363
+ if (object_sym == v) {
364
+ options.mode = ObjMode;
365
+ } else if (optimized_sym == v) {
366
+ options.mode = ObjMode;
367
+ } else if (generic_sym == v) {
368
+ options.mode = GenMode;
369
+ } else if (limited_sym == v) {
370
+ options.mode = LimMode;
371
+ } else {
372
+ rb_raise(rb_eArgError, ":mode must be :generic, :object, or :limited.\n");
373
+ }
374
+ }
375
+ if (Qnil != (v = rb_hash_lookup(h, effort_sym))) {
376
+ if (auto_define_sym == v) {
377
+ options.effort = AutoEffort;
378
+ } else if (tolerant_sym == v) {
379
+ options.effort = TolerantEffort;
380
+ } else if (strict_sym == v) {
381
+ options.effort = StrictEffort;
382
+ } else {
383
+ rb_raise(rb_eArgError, ":effort must be :strict, :tolerant, or :auto_define.\n");
384
+ }
385
+ }
386
+ if (Qnil != (v = rb_hash_lookup(h, trace_sym))) {
387
+ Check_Type(v, T_FIXNUM);
388
+ options.trace = FIX2INT(v);
389
+ }
390
+ if (Qnil != (v = rb_hash_lookup(h, symbolize_keys_sym))) {
391
+ options.sym_keys = (Qfalse == v) ? No : Yes;
392
+ }
384
393
  }
385
394
  switch (options.mode) {
386
395
  case ObjMode:
387
- obj = ox_parse(xml, ox_obj_callbacks, 0, options.trace, options.effort);
388
- break;
396
+ obj = ox_parse(xml, ox_obj_callbacks, 0, &options);
397
+ break;
389
398
  case GenMode:
390
- obj = ox_parse(xml, ox_gen_callbacks, 0, options.trace, options.effort);
391
- break;
399
+ obj = ox_parse(xml, ox_gen_callbacks, 0, &options);
400
+ break;
392
401
  case LimMode:
393
- obj = ox_parse(xml, ox_limited_callbacks, 0, options.trace, options.effort);
394
- break;
402
+ obj = ox_parse(xml, ox_limited_callbacks, 0, &options);
403
+ break;
395
404
  case NoMode:
396
- obj = ox_parse(xml, ox_nomode_callbacks, 0, options.trace, options.effort);
397
- break;
405
+ obj = ox_parse(xml, ox_nomode_callbacks, 0, &options);
406
+ break;
398
407
  default:
399
- obj = ox_parse(xml, ox_gen_callbacks, 0, options.trace, options.effort);
400
- break;
408
+ obj = ox_parse(xml, ox_gen_callbacks, 0, &options);
409
+ break;
401
410
  }
402
411
  return obj;
403
412
  }
@@ -418,10 +427,11 @@ load(char *xml, int argc, VALUE *argv, VALUE self) {
418
427
  * - *:tolerant* - return nil for missing classes and modules
419
428
  * - *:auto_define* - auto define missing classes and modules
420
429
  * @param [Fixnum] :trace trace level as a Fixnum, default: 0 (silent)
430
+ * @param [true|false|nil] :symbolize_keys symbolize element attribute keys or leave as Strings
421
431
  */
422
432
  static VALUE
423
433
  load_str(int argc, VALUE *argv, VALUE self) {
424
- char *xml;
434
+ char *xml;
425
435
  size_t len;
426
436
  VALUE obj;
427
437
 
@@ -444,7 +454,7 @@ load_str(int argc, VALUE *argv, VALUE self) {
444
454
  /* call-seq: load_file(file_path, options) => Ox::Document or Ox::Element or Object
445
455
  *
446
456
  * Parses and XML document from a file into an Ox::Document, or Ox::Element,
447
- * or Object depending on the options. Raises an exception if the XML is
457
+ * or Object depending on the options. Raises an exception if the XML is
448
458
  * malformed or the classes specified are not valid.
449
459
  * @param [String] file_path file path to read the XML document from
450
460
  * @param [Hash] options load options
@@ -457,6 +467,7 @@ load_str(int argc, VALUE *argv, VALUE self) {
457
467
  * - *:tolerant* - return nil for missing classes and modules
458
468
  * - *:auto_define* - auto define missing classes and modules
459
469
  * @param [Fixnum] :trace trace level as a Fixnum, default: 0 (silent)
470
+ * @param [true|false|nil] :symbolize_keys symbolize element attribute keys or leave as Strings
460
471
  */
461
472
  static VALUE
462
473
  load_file(int argc, VALUE *argv, VALUE self) {
@@ -469,7 +480,7 @@ load_file(int argc, VALUE *argv, VALUE self) {
469
480
  Check_Type(*argv, T_STRING);
470
481
  path = StringValuePtr(*argv);
471
482
  if (0 == (f = fopen(path, "r"))) {
472
- rb_raise(rb_eIOError, "%s\n", strerror(errno));
483
+ rb_raise(rb_eIOError, "%s\n", strerror(errno));
473
484
  }
474
485
  fseek(f, 0, SEEK_END);
475
486
  len = ftell(f);
@@ -480,8 +491,8 @@ load_file(int argc, VALUE *argv, VALUE self) {
480
491
  }
481
492
  fseek(f, 0, SEEK_SET);
482
493
  if (len != fread(xml, 1, len, f)) {
483
- fclose(f);
484
- rb_raise(rb_eLoadError, "Failed to read %ld bytes from %s.\n", (long)len, path);
494
+ fclose(f);
495
+ rb_raise(rb_eLoadError, "Failed to read %ld bytes from %s.\n", (long)len, path);
485
496
  }
486
497
  fclose(f);
487
498
  xml[len] = '\0';
@@ -506,15 +517,15 @@ sax_parse(int argc, VALUE *argv, VALUE self) {
506
517
  int convert = 0;
507
518
 
508
519
  if (argc < 2) {
509
- rb_raise(rb_eArgError, "Wrong number of arguments to sax_parse.\n");
520
+ rb_raise(rb_eArgError, "Wrong number of arguments to sax_parse.\n");
510
521
  }
511
522
  if (3 <= argc && rb_cHash == rb_obj_class(argv[2])) {
512
- VALUE h = argv[2];
513
- VALUE v;
514
-
515
- if (Qnil != (v = rb_hash_lookup(h, convert_special_sym))) {
516
- convert = (Qtrue == v);
517
- }
523
+ VALUE h = argv[2];
524
+ VALUE v;
525
+
526
+ if (Qnil != (v = rb_hash_lookup(h, convert_special_sym))) {
527
+ convert = (Qtrue == v);
528
+ }
518
529
  }
519
530
  ox_sax_parse(argv[0], argv[1], convert);
520
531
 
@@ -523,61 +534,61 @@ sax_parse(int argc, VALUE *argv, VALUE self) {
523
534
 
524
535
  static void
525
536
  parse_dump_options(VALUE ropts, Options copts) {
526
- struct _YesNoOpt ynos[] = {
527
- { with_xml_sym, &copts->with_xml },
528
- { with_dtd_sym, &copts->with_dtd },
529
- { with_instruct_sym, &copts->with_instruct },
530
- { xsd_date_sym, &copts->xsd_date },
531
- { circular_sym, &copts->circular },
532
- { Qnil, 0 }
537
+ struct _YesNoOpt ynos[] = {
538
+ { with_xml_sym, &copts->with_xml },
539
+ { with_dtd_sym, &copts->with_dtd },
540
+ { with_instruct_sym, &copts->with_instruct },
541
+ { xsd_date_sym, &copts->xsd_date },
542
+ { circular_sym, &copts->circular },
543
+ { Qnil, 0 }
533
544
  };
534
- YesNoOpt o;
545
+ YesNoOpt o;
535
546
 
536
547
  if (rb_cHash == rb_obj_class(ropts)) {
537
- VALUE v;
538
-
539
- if (Qnil != (v = rb_hash_lookup(ropts, indent_sym))) {
540
- if (rb_cFixnum != rb_obj_class(v)) {
541
- rb_raise(rb_eArgError, ":indent must be a Fixnum.\n");
542
- }
543
- copts->indent = NUM2INT(v);
544
- }
545
- if (Qnil != (v = rb_hash_lookup(ropts, trace_sym))) {
546
- if (rb_cFixnum != rb_obj_class(v)) {
547
- rb_raise(rb_eArgError, ":trace must be a Fixnum.\n");
548
- }
549
- copts->trace = NUM2INT(v);
550
- }
551
- if (Qnil != (v = rb_hash_lookup(ropts, ox_encoding_sym))) {
552
- if (rb_cString != rb_obj_class(v)) {
553
- rb_raise(rb_eArgError, ":encoding must be a String.\n");
554
- }
555
- strncpy(copts->encoding, StringValuePtr(v), sizeof(copts->encoding) - 1);
556
- }
557
- if (Qnil != (v = rb_hash_lookup(ropts, effort_sym))) {
558
- if (auto_define_sym == v) {
559
- copts->effort = AutoEffort;
560
- } else if (tolerant_sym == v) {
561
- copts->effort = TolerantEffort;
562
- } else if (strict_sym == v) {
563
- copts->effort = StrictEffort;
564
- } else {
565
- rb_raise(rb_eArgError, ":effort must be :strict, :tolerant, or :auto_define.\n");
566
- }
567
- }
568
- for (o = ynos; 0 != o->attr; o++) {
569
- if (Qnil != (v = rb_hash_lookup(ropts, o->sym))) {
570
- VALUE c = rb_obj_class(v);
571
-
572
- if (rb_cTrueClass == c) {
573
- *o->attr = Yes;
574
- } else if (rb_cFalseClass == c) {
575
- *o->attr = No;
576
- } else {
577
- rb_raise(rb_eArgError, "%s must be true or false.\n", rb_id2name(SYM2ID(o->sym)));
578
- }
579
- }
580
- }
548
+ VALUE v;
549
+
550
+ if (Qnil != (v = rb_hash_lookup(ropts, indent_sym))) {
551
+ if (rb_cFixnum != rb_obj_class(v)) {
552
+ rb_raise(rb_eArgError, ":indent must be a Fixnum.\n");
553
+ }
554
+ copts->indent = NUM2INT(v);
555
+ }
556
+ if (Qnil != (v = rb_hash_lookup(ropts, trace_sym))) {
557
+ if (rb_cFixnum != rb_obj_class(v)) {
558
+ rb_raise(rb_eArgError, ":trace must be a Fixnum.\n");
559
+ }
560
+ copts->trace = NUM2INT(v);
561
+ }
562
+ if (Qnil != (v = rb_hash_lookup(ropts, ox_encoding_sym))) {
563
+ if (rb_cString != rb_obj_class(v)) {
564
+ rb_raise(rb_eArgError, ":encoding must be a String.\n");
565
+ }
566
+ strncpy(copts->encoding, StringValuePtr(v), sizeof(copts->encoding) - 1);
567
+ }
568
+ if (Qnil != (v = rb_hash_lookup(ropts, effort_sym))) {
569
+ if (auto_define_sym == v) {
570
+ copts->effort = AutoEffort;
571
+ } else if (tolerant_sym == v) {
572
+ copts->effort = TolerantEffort;
573
+ } else if (strict_sym == v) {
574
+ copts->effort = StrictEffort;
575
+ } else {
576
+ rb_raise(rb_eArgError, ":effort must be :strict, :tolerant, or :auto_define.\n");
577
+ }
578
+ }
579
+ for (o = ynos; 0 != o->attr; o++) {
580
+ if (Qnil != (v = rb_hash_lookup(ropts, o->sym))) {
581
+ VALUE c = rb_obj_class(v);
582
+
583
+ if (rb_cTrueClass == c) {
584
+ *o->attr = Yes;
585
+ } else if (rb_cFalseClass == c) {
586
+ *o->attr = No;
587
+ } else {
588
+ rb_raise(rb_eArgError, "%s must be true or false.\n", rb_id2name(SYM2ID(o->sym)));
589
+ }
590
+ }
591
+ }
581
592
  }
582
593
  }
583
594
 
@@ -595,20 +606,20 @@ parse_dump_options(VALUE ropts, Options copts) {
595
606
  */
596
607
  static VALUE
597
608
  dump(int argc, VALUE *argv, VALUE self) {
598
- char *xml;
599
- struct _Options copts = ox_default_options;
600
- VALUE rstr;
609
+ char *xml;
610
+ struct _Options copts = ox_default_options;
611
+ VALUE rstr;
601
612
 
602
613
  if (2 == argc) {
603
- parse_dump_options(argv[1], &copts);
614
+ parse_dump_options(argv[1], &copts);
604
615
  }
605
616
  if (0 == (xml = ox_write_obj_to_str(*argv, &copts))) {
606
- rb_raise(rb_eNoMemError, "Not enough memory.\n");
617
+ rb_raise(rb_eNoMemError, "Not enough memory.\n");
607
618
  }
608
619
  rstr = rb_str_new2(xml);
609
620
  #if HAS_ENCODING_SUPPORT
610
621
  if ('\0' != *copts.encoding) {
611
- rb_enc_associate(rstr, rb_enc_find(copts.encoding));
622
+ rb_enc_associate(rstr, rb_enc_find(copts.encoding));
612
623
  }
613
624
  #endif
614
625
  xfree(xml);
@@ -631,10 +642,10 @@ dump(int argc, VALUE *argv, VALUE self) {
631
642
  */
632
643
  static VALUE
633
644
  to_file(int argc, VALUE *argv, VALUE self) {
634
- struct _Options copts = ox_default_options;
645
+ struct _Options copts = ox_default_options;
635
646
 
636
647
  if (3 == argc) {
637
- parse_dump_options(argv[2], &copts);
648
+ parse_dump_options(argv[2], &copts);
638
649
  }
639
650
  Check_Type(*argv, T_STRING);
640
651
  ox_write_obj_to_file(argv[1], StringValuePtr(*argv), &copts);
@@ -642,7 +653,7 @@ to_file(int argc, VALUE *argv, VALUE self) {
642
653
  return Qnil;
643
654
  }
644
655
 
645
- extern void ox_cache_test(void);
656
+ extern void ox_cache_test(void);
646
657
 
647
658
  static VALUE
648
659
  cache_test(VALUE self) {
@@ -650,7 +661,7 @@ cache_test(VALUE self) {
650
661
  return Qnil;
651
662
  }
652
663
 
653
- extern void ox_cache8_test(void);
664
+ extern void ox_cache8_test(void);
654
665
 
655
666
  static VALUE
656
667
  cache8_test(VALUE self) {
@@ -722,26 +733,27 @@ void Init_ox() {
722
733
  ox_struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
723
734
  ox_stringio_class = rb_const_get(rb_cObject, rb_intern("StringIO"));
724
735
 
736
+ auto_define_sym = ID2SYM(rb_intern("auto_define")); rb_gc_register_address(&auto_define_sym);
737
+ auto_sym = ID2SYM(rb_intern("auto")); rb_gc_register_address(&auto_sym);
738
+ circular_sym = ID2SYM(rb_intern("circular")); rb_gc_register_address(&circular_sym);
739
+ convert_special_sym = ID2SYM(rb_intern("convert_special")); rb_gc_register_address(&convert_special_sym);
740
+ effort_sym = ID2SYM(rb_intern("effort")); rb_gc_register_address(&effort_sym);
741
+ generic_sym = ID2SYM(rb_intern("generic")); rb_gc_register_address(&generic_sym);
742
+ indent_sym = ID2SYM(rb_intern("indent")); rb_gc_register_address(&indent_sym);
743
+ limited_sym = ID2SYM(rb_intern("limited")); rb_gc_register_address(&limited_sym);
744
+ mode_sym = ID2SYM(rb_intern("mode")); rb_gc_register_address(&mode_sym);
745
+ object_sym = ID2SYM(rb_intern("object")); rb_gc_register_address(&object_sym);
746
+ opt_format_sym = ID2SYM(rb_intern("opt_format")); rb_gc_register_address(&opt_format_sym);
747
+ optimized_sym = ID2SYM(rb_intern("optimized")); rb_gc_register_address(&optimized_sym);
725
748
  ox_encoding_sym = ID2SYM(rb_intern("encoding")); rb_gc_register_address(&ox_encoding_sym);
726
- indent_sym = ID2SYM(rb_intern("indent")); rb_gc_register_address(&indent_sym);
727
- xsd_date_sym = ID2SYM(rb_intern("xsd_date")); rb_gc_register_address(&xsd_date_sym);
728
- opt_format_sym = ID2SYM(rb_intern("opt_format")); rb_gc_register_address(&opt_format_sym);
729
- mode_sym = ID2SYM(rb_intern("mode")); rb_gc_register_address(&mode_sym);
730
- auto_sym = ID2SYM(rb_intern("auto")); rb_gc_register_address(&auto_sym);
731
- optimized_sym = ID2SYM(rb_intern("optimized")); rb_gc_register_address(&optimized_sym);
732
- object_sym = ID2SYM(rb_intern("object")); rb_gc_register_address(&object_sym);
733
- circular_sym = ID2SYM(rb_intern("circular")); rb_gc_register_address(&circular_sym);
734
- generic_sym = ID2SYM(rb_intern("generic")); rb_gc_register_address(&generic_sym);
735
- limited_sym = ID2SYM(rb_intern("limited")); rb_gc_register_address(&limited_sym);
736
- trace_sym = ID2SYM(rb_intern("trace")); rb_gc_register_address(&trace_sym);
737
- effort_sym = ID2SYM(rb_intern("effort")); rb_gc_register_address(&effort_sym);
738
- strict_sym = ID2SYM(rb_intern("strict")); rb_gc_register_address(&strict_sym);
739
- tolerant_sym = ID2SYM(rb_intern("tolerant")); rb_gc_register_address(&tolerant_sym);
740
- auto_define_sym = ID2SYM(rb_intern("auto_define")); rb_gc_register_address(&auto_define_sym);
741
- with_dtd_sym = ID2SYM(rb_intern("with_dtd")); rb_gc_register_address(&with_dtd_sym);
749
+ strict_sym = ID2SYM(rb_intern("strict")); rb_gc_register_address(&strict_sym);
750
+ symbolize_keys_sym = ID2SYM(rb_intern("symbolize_keys")); rb_gc_register_address(&symbolize_keys_sym);
751
+ tolerant_sym = ID2SYM(rb_intern("tolerant")); rb_gc_register_address(&tolerant_sym);
752
+ trace_sym = ID2SYM(rb_intern("trace")); rb_gc_register_address(&trace_sym);
753
+ with_dtd_sym = ID2SYM(rb_intern("with_dtd")); rb_gc_register_address(&with_dtd_sym);
742
754
  with_instruct_sym = ID2SYM(rb_intern("with_instructions")); rb_gc_register_address(&with_instruct_sym);
743
- with_xml_sym = ID2SYM(rb_intern("with_xml")); rb_gc_register_address(&with_xml_sym);
744
- convert_special_sym = ID2SYM(rb_intern("convert_special")); rb_gc_register_address(&convert_special_sym);
755
+ with_xml_sym = ID2SYM(rb_intern("with_xml")); rb_gc_register_address(&with_xml_sym);
756
+ xsd_date_sym = ID2SYM(rb_intern("xsd_date")); rb_gc_register_address(&xsd_date_sym);
745
757
 
746
758
  ox_empty_string = rb_str_new2(""); rb_gc_register_address(&ox_empty_string);
747
759
  ox_zero_fixnum = INT2NUM(0); rb_gc_register_address(&ox_zero_fixnum);
@@ -765,16 +777,16 @@ void Init_ox() {
765
777
 
766
778
  void
767
779
  _ox_raise_error(const char *msg, const char *xml, const char *current, const char* file, int line) {
768
- int xline = 1;
769
- int col = 1;
780
+ int xline = 1;
781
+ int col = 1;
770
782
 
771
783
  for (; xml < current && '\n' != *current; current--) {
772
- col++;
784
+ col++;
773
785
  }
774
786
  for (; xml < current; current--) {
775
- if ('\n' == *current) {
776
- xline++;
777
- }
787
+ if ('\n' == *current) {
788
+ xline++;
789
+ }
778
790
  }
779
791
  rb_raise(rb_eSyntaxError, "%s at line %d, column %d [%s:%d]\n", msg, xline, col, file, line);
780
792
  }