Yaml.java revision 8db67301d5eeaa81c187f0ad5e4c55fa6d59c1b3
1/** 2 * Copyright (c) 2008-2011, http://www.snakeyaml.org 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package org.yaml.snakeyaml; 18 19import java.io.IOException; 20import java.io.InputStream; 21import java.io.Reader; 22import java.io.StringReader; 23import java.io.StringWriter; 24import java.io.Writer; 25import java.util.ArrayList; 26import java.util.Iterator; 27import java.util.List; 28import java.util.regex.Pattern; 29 30import org.yaml.snakeyaml.DumperOptions.FlowStyle; 31import org.yaml.snakeyaml.composer.Composer; 32import org.yaml.snakeyaml.constructor.BaseConstructor; 33import org.yaml.snakeyaml.constructor.Constructor; 34import org.yaml.snakeyaml.emitter.Emitable; 35import org.yaml.snakeyaml.emitter.Emitter; 36import org.yaml.snakeyaml.error.YAMLException; 37import org.yaml.snakeyaml.events.Event; 38import org.yaml.snakeyaml.introspector.BeanAccess; 39import org.yaml.snakeyaml.nodes.Node; 40import org.yaml.snakeyaml.nodes.Tag; 41import org.yaml.snakeyaml.parser.Parser; 42import org.yaml.snakeyaml.parser.ParserImpl; 43import org.yaml.snakeyaml.reader.StreamReader; 44import org.yaml.snakeyaml.reader.UnicodeReader; 45import org.yaml.snakeyaml.representer.Representer; 46import org.yaml.snakeyaml.resolver.Resolver; 47import org.yaml.snakeyaml.serializer.Serializer; 48 49/** 50 * Public YAML interface. Each Thread must have its own instance. 51 */ 52public class Yaml { 53 protected final Resolver resolver; 54 private String name; 55 protected BaseConstructor constructor; 56 protected Representer representer; 57 protected DumperOptions dumperOptions; 58 protected LoaderOptions loaderOptions; 59 60 /** 61 * Create Yaml instance. It is safe to create a few instances and use them 62 * in different Threads. 63 */ 64 public Yaml() { 65 this(new Constructor(), new LoaderOptions(), new Representer(), new DumperOptions(), 66 new Resolver()); 67 } 68 69 public Yaml(LoaderOptions loaderOptions) { 70 this(new Constructor(), loaderOptions, new Representer(), new DumperOptions(), 71 new Resolver()); 72 } 73 74 /** 75 * Create Yaml instance. 76 * 77 * @param dumperOptions 78 * DumperOptions to configure outgoing objects 79 */ 80 public Yaml(DumperOptions dumperOptions) { 81 this(new Constructor(), new Representer(), dumperOptions); 82 } 83 84 /** 85 * Create Yaml instance. It is safe to create a few instances and use them 86 * in different Threads. 87 * 88 * @param representer 89 * Representer to emit outgoing objects 90 */ 91 public Yaml(Representer representer) { 92 this(new Constructor(), representer); 93 } 94 95 /** 96 * Create Yaml instance. It is safe to create a few instances and use them 97 * in different Threads. 98 * 99 * @param constructor 100 * BaseConstructor to construct incoming documents 101 */ 102 public Yaml(BaseConstructor constructor) { 103 this(constructor, new Representer()); 104 } 105 106 /** 107 * Create Yaml instance. It is safe to create a few instances and use them 108 * in different Threads. 109 * 110 * @param constructor 111 * BaseConstructor to construct incoming documents 112 * @param representer 113 * Representer to emit outgoing objects 114 */ 115 public Yaml(BaseConstructor constructor, Representer representer) { 116 this(constructor, representer, new DumperOptions()); 117 } 118 119 /** 120 * Create Yaml instance. It is safe to create a few instances and use them 121 * in different Threads. 122 * 123 * @param representer 124 * Representer to emit outgoing objects 125 * @param dumperOptions 126 * DumperOptions to configure outgoing objects 127 */ 128 public Yaml(Representer representer, DumperOptions dumperOptions) { 129 this(new Constructor(), representer, dumperOptions, new Resolver()); 130 } 131 132 /** 133 * Create Yaml instance. It is safe to create a few instances and use them 134 * in different Threads. 135 * 136 * @param constructor 137 * BaseConstructor to construct incoming documents 138 * @param representer 139 * Representer to emit outgoing objects 140 * @param dumperOptions 141 * DumperOptions to configure outgoing objects 142 */ 143 public Yaml(BaseConstructor constructor, Representer representer, DumperOptions dumperOptions) { 144 this(constructor, representer, dumperOptions, new Resolver()); 145 } 146 147 /** 148 * Create Yaml instance. It is safe to create a few instances and use them 149 * in different Threads. 150 * 151 * @param constructor 152 * BaseConstructor to construct incoming documents 153 * @param representer 154 * Representer to emit outgoing objects 155 * @param dumperOptions 156 * DumperOptions to configure outgoing objects 157 * @param resolver 158 * Resolver to detect implicit type 159 */ 160 public Yaml(BaseConstructor constructor, Representer representer, DumperOptions dumperOptions, 161 Resolver resolver) { 162 this(constructor, new LoaderOptions(), representer, dumperOptions, resolver); 163 } 164 165 /** 166 * Create Yaml instance. It is safe to create a few instances and use them 167 * in different Threads. 168 * 169 * @param constructor 170 * BaseConstructor to construct incoming documents 171 * @param loaderOptions 172 * LoaderOptions to control construction process 173 * @param representer 174 * Representer to emit outgoing objects 175 * @param dumperOptions 176 * DumperOptions to configure outgoing objects 177 * @param resolver 178 * Resolver to detect implicit type 179 */ 180 public Yaml(BaseConstructor constructor, LoaderOptions loaderOptions, Representer representer, 181 DumperOptions dumperOptions, Resolver resolver) { 182 if (!constructor.isExplicitPropertyUtils()) { 183 constructor.setPropertyUtils(representer.getPropertyUtils()); 184 } else if (!representer.isExplicitPropertyUtils()) { 185 representer.setPropertyUtils(constructor.getPropertyUtils()); 186 } 187 this.constructor = constructor; 188 this.loaderOptions = loaderOptions; 189 representer.setDefaultFlowStyle(dumperOptions.getDefaultFlowStyle()); 190 representer.setDefaultScalarStyle(dumperOptions.getDefaultScalarStyle()); 191 representer.getPropertyUtils().setAllowReadOnlyProperties( 192 dumperOptions.isAllowReadOnlyProperties()); 193 this.representer = representer; 194 this.dumperOptions = dumperOptions; 195 this.resolver = resolver; 196 this.name = "Yaml:" + System.identityHashCode(this); 197 } 198 199 /** 200 * Serialize a Java object into a YAML String. 201 * 202 * @param data 203 * Java object to be Serialized to YAML 204 * @return YAML String 205 */ 206 public String dump(Object data) { 207 List<Object> list = new ArrayList<Object>(1); 208 list.add(data); 209 return dumpAll(list.iterator()); 210 } 211 212 /** 213 * Produce the corresponding representation tree for a given Object. 214 * 215 * @see http://yaml.org/spec/1.1/#id859333 216 * @param data 217 * instance to build the representation tree for 218 * @return representation tree 219 */ 220 public Node represent(Object data) { 221 return representer.represent(data); 222 } 223 224 /** 225 * Serialize a sequence of Java objects into a YAML String. 226 * 227 * @param data 228 * Iterator with Objects 229 * @return YAML String with all the objects in proper sequence 230 */ 231 public String dumpAll(Iterator<? extends Object> data) { 232 StringWriter buffer = new StringWriter(); 233 dumpAll(data, buffer); 234 return buffer.toString(); 235 } 236 237 /** 238 * Serialize a Java object into a YAML stream. 239 * 240 * @param data 241 * Java object to be serialized to YAML 242 * @param output 243 * stream to write to 244 */ 245 public void dump(Object data, Writer output) { 246 List<Object> list = new ArrayList<Object>(1); 247 list.add(data); 248 dumpAll(list.iterator(), output); 249 } 250 251 /** 252 * Serialize a sequence of Java objects into a YAML stream. 253 * 254 * @param data 255 * Iterator with Objects 256 * @param output 257 * stream to write to 258 */ 259 @SuppressWarnings("deprecation") 260 public void dumpAll(Iterator<? extends Object> data, Writer output) { 261 dumpAll(data, output, dumperOptions.getExplicitRoot()); 262 } 263 264 private void dumpAll(Iterator<? extends Object> data, Writer output, Tag rootTag) { 265 Serializer serializer = new Serializer(new Emitter(output, dumperOptions), resolver, 266 dumperOptions, rootTag); 267 try { 268 serializer.open(); 269 while (data.hasNext()) { 270 Node node = representer.represent(data.next()); 271 serializer.serialize(node); 272 } 273 serializer.close(); 274 } catch (java.io.IOException e) { 275 throw new YAMLException(e); 276 } 277 } 278 279 /** 280 * <p> 281 * Serialize a Java object into a YAML string. Override the default root tag 282 * with <code>rootTag</code>. 283 * </p> 284 * 285 * <p> 286 * This method is similar to <code>Yaml.dump(data)</code> except that the 287 * root tag for the whole document is replaced with the given tag. This has 288 * two main uses. 289 * </p> 290 * 291 * <p> 292 * First, if the root tag is replaced with a standard YAML tag, such as 293 * <code>Tag.MAP</code>, then the object will be dumped as a map. The root 294 * tag will appear as <code>!!map</code>, or blank (implicit !!map). 295 * </p> 296 * 297 * <p> 298 * Second, if the root tag is replaced by a different custom tag, then the 299 * document appears to be a different type when loaded. For example, if an 300 * instance of MyClass is dumped with the tag !!YourClass, then it will be 301 * handled as an instance of YourClass when loaded. 302 * </p> 303 * 304 * @param data 305 * Java object to be serialized to YAML 306 * @param rootTag 307 * the tag for the whole YAML document. The tag should be Tag.MAP 308 * for a JavaBean to make the tag disappear (to use implicit tag 309 * !!map) 310 * @param flowStyle 311 * flow style for the whole document. See Chapter 10. Collection 312 * Styles http://yaml.org/spec/1.1/#id930798 313 * 314 * @return YAML String 315 */ 316 public String dumpAs(Object data, Tag rootTag, FlowStyle flowStyle) { 317 FlowStyle oldStyle = representer.getDefaultFlowStyle(); 318 representer.setDefaultFlowStyle(flowStyle); 319 List<Object> list = new ArrayList<Object>(1); 320 list.add(data); 321 StringWriter buffer = new StringWriter(); 322 dumpAll(list.iterator(), buffer, rootTag); 323 representer.setDefaultFlowStyle(oldStyle); 324 return buffer.toString(); 325 } 326 327 /** 328 * <p> 329 * Serialize a Java object into a YAML string. Override the default root tag 330 * with <code>Tag.MAP</code>. 331 * </p> 332 * <p> 333 * This method is similar to <code>Yaml.dump(data)</code> except that the 334 * root tag for the whole document is replaced with <code>Tag.MAP</code> tag 335 * (implicit !!map). 336 * </p> 337 * <p> 338 * Block Mapping is used as the collection style. See 10.2.2. Block Mappings 339 * (http://yaml.org/spec/1.1/#id934537) 340 * </p> 341 * 342 * @param data 343 * Java object to be serialized to YAML 344 * @return YAML String 345 */ 346 public String dumpAs(Object data) { 347 return dumpAs(data, Tag.MAP, FlowStyle.BLOCK); 348 } 349 350 /** 351 * Serialize the representation tree into Events. 352 * 353 * @see http://yaml.org/spec/1.1/#id859333 354 * @param data 355 * representation tree 356 * @return Event list 357 */ 358 public List<Event> serialize(Node data) { 359 SilentEmitter emitter = new SilentEmitter(); 360 @SuppressWarnings("deprecation") 361 Serializer serializer = new Serializer(emitter, resolver, dumperOptions, 362 dumperOptions.getExplicitRoot()); 363 try { 364 serializer.open(); 365 serializer.serialize(data); 366 serializer.close(); 367 } catch (java.io.IOException e) { 368 throw new YAMLException(e); 369 } 370 return emitter.getEvents(); 371 } 372 373 private class SilentEmitter implements Emitable { 374 private List<Event> events = new ArrayList<Event>(100); 375 376 public List<Event> getEvents() { 377 return events; 378 } 379 380 public void emit(Event event) throws IOException { 381 events.add(event); 382 } 383 } 384 385 /** 386 * Parse the only YAML document in a String and produce the corresponding 387 * Java object. (Because the encoding in known BOM is not respected.) 388 * 389 * @param yaml 390 * YAML data to load from (BOM must not be present) 391 * @return parsed object 392 */ 393 public Object load(String yaml) { 394 return loadFromReader(new StreamReader(yaml), Object.class); 395 } 396 397 /** 398 * Parse the only YAML document in a stream and produce the corresponding 399 * Java object. 400 * 401 * @param io 402 * data to load from (BOM is respected and removed) 403 * @return parsed object 404 */ 405 public Object load(InputStream io) { 406 return loadFromReader(new StreamReader(new UnicodeReader(io)), Object.class); 407 } 408 409 /** 410 * Parse the only YAML document in a stream and produce the corresponding 411 * Java object. 412 * 413 * @param io 414 * data to load from (BOM must not be present) 415 * @return parsed object 416 */ 417 public Object load(Reader io) { 418 return loadFromReader(new StreamReader(io), Object.class); 419 } 420 421 /** 422 * Parse the only YAML document in a stream and produce the corresponding 423 * Java object. 424 * 425 * @param <T> 426 * Class is defined by the second argument 427 * @param io 428 * data to load from (BOM must not be present) 429 * @param type 430 * Class of the object to be created 431 * @return parsed object 432 */ 433 @SuppressWarnings("unchecked") 434 public <T> T loadAs(Reader io, Class<T> type) { 435 return (T) loadFromReader(new StreamReader(io), type); 436 } 437 438 /** 439 * Parse the only YAML document in a String and produce the corresponding 440 * Java object. (Because the encoding in known BOM is not respected.) 441 * 442 * @param <T> 443 * Class is defined by the second argument 444 * @param yaml 445 * YAML data to load from (BOM must not be present) 446 * @param type 447 * Class of the object to be created 448 * @return parsed object 449 */ 450 @SuppressWarnings("unchecked") 451 public <T> T loadAs(String yaml, Class<T> type) { 452 return (T) loadFromReader(new StreamReader(yaml), type); 453 } 454 455 /** 456 * Parse the only YAML document in a stream and produce the corresponding 457 * Java object. 458 * 459 * @param <T> 460 * Class is defined by the second argument 461 * @param input 462 * data to load from (BOM is respected and removed) 463 * @param type 464 * Class of the object to be created 465 * @return parsed object 466 */ 467 @SuppressWarnings("unchecked") 468 public <T> T loadAs(InputStream input, Class<T> type) { 469 return (T) loadFromReader(new StreamReader(new UnicodeReader(input)), type); 470 } 471 472 private Object loadFromReader(StreamReader sreader, Class<?> type) { 473 Composer composer = new Composer(new ParserImpl(sreader), resolver); 474 constructor.setComposer(composer); 475 return constructor.getSingleData(type); 476 } 477 478 /** 479 * Parse all YAML documents in a String and produce corresponding Java 480 * objects. The documents are parsed only when the iterator is invoked. 481 * 482 * @param yaml 483 * YAML data to load from (BOM must not be present) 484 * @return an iterator over the parsed Java objects in this String in proper 485 * sequence 486 */ 487 public Iterable<Object> loadAll(Reader yaml) { 488 Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver); 489 constructor.setComposer(composer); 490 Iterator<Object> result = new Iterator<Object>() { 491 public boolean hasNext() { 492 return constructor.checkData(); 493 } 494 495 public Object next() { 496 return constructor.getData(); 497 } 498 499 public void remove() { 500 throw new UnsupportedOperationException(); 501 } 502 }; 503 return new YamlIterable(result); 504 } 505 506 private class YamlIterable implements Iterable<Object> { 507 private Iterator<Object> iterator; 508 509 public YamlIterable(Iterator<Object> iterator) { 510 this.iterator = iterator; 511 } 512 513 public Iterator<Object> iterator() { 514 return iterator; 515 } 516 517 } 518 519 /** 520 * Parse all YAML documents in a String and produce corresponding Java 521 * objects. (Because the encoding in known BOM is not respected.) The 522 * documents are parsed only when the iterator is invoked. 523 * 524 * @param yaml 525 * YAML data to load from (BOM must not be present) 526 * @return an iterator over the parsed Java objects in this String in proper 527 * sequence 528 */ 529 public Iterable<Object> loadAll(String yaml) { 530 return loadAll(new StringReader(yaml)); 531 } 532 533 /** 534 * Parse all YAML documents in a stream and produce corresponding Java 535 * objects. The documents are parsed only when the iterator is invoked. 536 * 537 * @param yaml 538 * YAML data to load from (BOM is respected and ignored) 539 * @return an iterator over the parsed Java objects in this stream in proper 540 * sequence 541 */ 542 public Iterable<Object> loadAll(InputStream yaml) { 543 return loadAll(new UnicodeReader(yaml)); 544 } 545 546 /** 547 * Parse the first YAML document in a stream and produce the corresponding 548 * representation tree. (This is the opposite of the represent() method) 549 * 550 * @see http://yaml.org/spec/1.1/#id859333 551 * @param yaml 552 * YAML document 553 * @return parsed root Node for the specified YAML document 554 */ 555 public Node compose(Reader yaml) { 556 Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver); 557 constructor.setComposer(composer); 558 return composer.getSingleNode(); 559 } 560 561 /** 562 * Parse all YAML documents in a stream and produce corresponding 563 * representation trees. 564 * 565 * @see http://yaml.org/spec/1.1/#id859333 566 * @param yaml 567 * stream of YAML documents 568 * @return parsed root Nodes for all the specified YAML documents 569 */ 570 public Iterable<Node> composeAll(Reader yaml) { 571 final Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver); 572 constructor.setComposer(composer); 573 Iterator<Node> result = new Iterator<Node>() { 574 public boolean hasNext() { 575 return composer.checkNode(); 576 } 577 578 public Node next() { 579 return composer.getNode(); 580 } 581 582 public void remove() { 583 throw new UnsupportedOperationException(); 584 } 585 }; 586 return new NodeIterable(result); 587 } 588 589 private class NodeIterable implements Iterable<Node> { 590 private Iterator<Node> iterator; 591 592 public NodeIterable(Iterator<Node> iterator) { 593 this.iterator = iterator; 594 } 595 596 public Iterator<Node> iterator() { 597 return iterator; 598 } 599 } 600 601 /** 602 * Add an implicit scalar detector. If an implicit scalar value matches the 603 * given regexp, the corresponding tag is assigned to the scalar. 604 * 605 * @deprecated use Tag instead of String 606 * @param tag 607 * tag to assign to the node 608 * @param regexp 609 * regular expression to match against 610 * @param first 611 * a sequence of possible initial characters or null (which means 612 * any). 613 * 614 */ 615 public void addImplicitResolver(String tag, Pattern regexp, String first) { 616 addImplicitResolver(new Tag(tag), regexp, first); 617 } 618 619 /** 620 * Add an implicit scalar detector. If an implicit scalar value matches the 621 * given regexp, the corresponding tag is assigned to the scalar. 622 * 623 * @param tag 624 * tag to assign to the node 625 * @param regexp 626 * regular expression to match against 627 * @param first 628 * a sequence of possible initial characters or null (which means 629 * any). 630 */ 631 public void addImplicitResolver(Tag tag, Pattern regexp, String first) { 632 resolver.addImplicitResolver(tag, regexp, first); 633 } 634 635 @Override 636 public String toString() { 637 return name; 638 } 639 640 /** 641 * Get a meaningful name. It simplifies debugging in a multi-threaded 642 * environment. If nothing is set explicitly the address of the instance is 643 * returned. 644 * 645 * @return human readable name 646 */ 647 public String getName() { 648 return name; 649 } 650 651 /** 652 * Set a meaningful name to be shown in toString() 653 * 654 * @param name 655 * human readable name 656 */ 657 public void setName(String name) { 658 this.name = name; 659 } 660 661 /** 662 * Parse a YAML stream and produce parsing events. 663 * 664 * @see http://yaml.org/spec/1.1/#id859333 665 * @param yaml 666 * YAML document(s) 667 * @return parsed events 668 */ 669 public Iterable<Event> parse(Reader yaml) { 670 final Parser parser = new ParserImpl(new StreamReader(yaml)); 671 Iterator<Event> result = new Iterator<Event>() { 672 public boolean hasNext() { 673 return parser.peekEvent() != null; 674 } 675 676 public Event next() { 677 return parser.getEvent(); 678 } 679 680 public void remove() { 681 throw new UnsupportedOperationException(); 682 } 683 }; 684 return new EventIterable(result); 685 } 686 687 private class EventIterable implements Iterable<Event> { 688 private Iterator<Event> iterator; 689 690 public EventIterable(Iterator<Event> iterator) { 691 this.iterator = iterator; 692 } 693 694 public Iterator<Event> iterator() { 695 return iterator; 696 } 697 } 698 699 public void setBeanAccess(BeanAccess beanAccess) { 700 constructor.getPropertyUtils().setBeanAccess(beanAccess); 701 representer.getPropertyUtils().setBeanAccess(beanAccess); 702 } 703 704 // deprecated 705 /** 706 * @deprecated use with Constructor instead of Loader 707 */ 708 public Yaml(Loader loader) { 709 this(loader, new Dumper(new DumperOptions())); 710 } 711 712 /** 713 * @deprecated use with Constructor instead of Loader 714 */ 715 public Yaml(Loader loader, Dumper dumper) { 716 this(loader, dumper, new Resolver()); 717 } 718 719 /** 720 * @deprecated use with Constructor instead of Loader 721 */ 722 public Yaml(Loader loader, Dumper dumper, Resolver resolver) { 723 this(loader.constructor, dumper.representer, dumper.options, resolver); 724 } 725 726 /** 727 * Create Yaml instance. It is safe to create a few instances and use them 728 * in different Threads. 729 * 730 * @param dumper 731 * Dumper to emit outgoing objects 732 */ 733 @SuppressWarnings("deprecation") 734 public Yaml(Dumper dumper) { 735 this(new Constructor(), dumper.representer, dumper.options); 736 } 737} 738