/** * Copyright (c) 2008-2013, http://www.snakeyaml.org * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yaml.snakeyaml; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.regex.Pattern; import org.yaml.snakeyaml.DumperOptions.FlowStyle; import org.yaml.snakeyaml.composer.Composer; import org.yaml.snakeyaml.constructor.BaseConstructor; import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.emitter.Emitable; import org.yaml.snakeyaml.emitter.Emitter; import org.yaml.snakeyaml.error.YAMLException; import org.yaml.snakeyaml.events.Event; import org.yaml.snakeyaml.introspector.BeanAccess; import org.yaml.snakeyaml.nodes.Node; import org.yaml.snakeyaml.nodes.Tag; import org.yaml.snakeyaml.parser.Parser; import org.yaml.snakeyaml.parser.ParserImpl; import org.yaml.snakeyaml.reader.StreamReader; import org.yaml.snakeyaml.reader.UnicodeReader; import org.yaml.snakeyaml.representer.Representer; import org.yaml.snakeyaml.resolver.Resolver; import org.yaml.snakeyaml.serializer.Serializer; /** * Public YAML interface. Each Thread must have its own instance. */ public class Yaml { protected final Resolver resolver; private String name; protected BaseConstructor constructor; protected Representer representer; protected DumperOptions dumperOptions; /** * Create Yaml instance. It is safe to create a few instances and use them in different Threads. */ public Yaml() { this(new Constructor(), new Representer(), new DumperOptions(), new Resolver()); } /** * @deprecated For some reason */ @Deprecated public Yaml(final LoaderOptions loaderOptions) { this(new Constructor(), new Representer(), new DumperOptions(), new Resolver()); } /** * Create Yaml instance. * * @param dumperOptions * DumperOptions to configure outgoing objects */ public Yaml(final DumperOptions dumperOptions) { this(new Constructor(), new Representer(), dumperOptions); } /** * Create Yaml instance. It is safe to create a few instances and use them in different Threads. * * @param representer * Representer to emit outgoing objects */ public Yaml(final Representer representer) { this(new Constructor(), representer); } /** * Create Yaml instance. It is safe to create a few instances and use them in different Threads. * * @param constructor * BaseConstructor to construct incoming documents */ public Yaml(final BaseConstructor constructor) { this(constructor, new Representer()); } /** * Create Yaml instance. It is safe to create a few instances and use them in different Threads. * * @param constructor * BaseConstructor to construct incoming documents * @param representer * Representer to emit outgoing objects */ public Yaml(final BaseConstructor constructor, final Representer representer) { this(constructor, representer, new DumperOptions()); } /** * Create Yaml instance. It is safe to create a few instances and use them in different Threads. * * @param representer * Representer to emit outgoing objects * @param dumperOptions * DumperOptions to configure outgoing objects */ public Yaml(final Representer representer, final DumperOptions dumperOptions) { this(new Constructor(), representer, dumperOptions, new Resolver()); } /** * Create Yaml instance. It is safe to create a few instances and use them in different Threads. * * @param constructor * BaseConstructor to construct incoming documents * @param representer * Representer to emit outgoing objects * @param dumperOptions * DumperOptions to configure outgoing objects */ public Yaml(final BaseConstructor constructor, final Representer representer, final DumperOptions dumperOptions) { this(constructor, representer, dumperOptions, new Resolver()); } /** * Create Yaml instance. It is safe to create a few instances and use them in different Threads. * * @param constructor * BaseConstructor to construct incoming documents * @param representer * Representer to emit outgoing objects * @param dumperOptions * DumperOptions to configure outgoing objects * @param resolver * Resolver to detect implicit type */ public Yaml(final BaseConstructor constructor, final Representer representer, final DumperOptions dumperOptions, final Resolver resolver) { if (!constructor.isExplicitPropertyUtils()) { constructor.setPropertyUtils(representer.getPropertyUtils()); } else if (!representer.isExplicitPropertyUtils()) { representer.setPropertyUtils(constructor.getPropertyUtils()); } this.constructor = constructor; representer.setDefaultFlowStyle(dumperOptions.getDefaultFlowStyle()); representer.setDefaultScalarStyle(dumperOptions.getDefaultScalarStyle()); representer.getPropertyUtils().setAllowReadOnlyProperties(dumperOptions.isAllowReadOnlyProperties()); representer.setTimeZone(dumperOptions.getTimeZone()); this.representer = representer; this.dumperOptions = dumperOptions; this.resolver = resolver; this.name = "Yaml:" + System.identityHashCode(this); } /** * Create Yaml instance. It is safe to create a few instances and use them in different Threads. * * @param constructor * BaseConstructor to construct incoming documents * @param loaderOptions * LoaderOptions to control construction process (unused) * @param representer * Representer to emit outgoing objects * @param dumperOptions * DumperOptions to configure outgoing objects * @param resolver * Resolver to detect implicit type * @deprecated For some reason */ @Deprecated public Yaml(final BaseConstructor constructor, final LoaderOptions loaderOptions, final Representer representer, final DumperOptions dumperOptions, final Resolver resolver) { this(constructor, representer, dumperOptions, resolver); } /** * Serialize a Java object into a YAML String. * * @param data * Java object to be Serialized to YAML * @return YAML String */ public String dump(final Object data) { List<Object> list = new ArrayList<Object>(1); list.add(data); return dumpAll(list.iterator()); } /** * Produce the corresponding representation tree for a given Object. * * @see <a href="http://yaml.org/spec/1.1/#id859333">Figure 3.1. Processing Overview</a> * @param data * instance to build the representation tree for * @return representation tree */ public Node represent(final Object data) { return representer.represent(data); } /** * Serialize a sequence of Java objects into a YAML String. * * @param data * Iterator with Objects * @return YAML String with all the objects in proper sequence */ public String dumpAll(final Iterator<? extends Object> data) { StringWriter buffer = new StringWriter(); dumpAll(data, buffer); return buffer.toString(); } /** * Serialize a Java object into a YAML stream. * * @param data * Java object to be serialized to YAML * @param output * stream to write to */ public void dump(final Object data, final Writer output) { List<Object> list = new ArrayList<Object>(1); list.add(data); dumpAll(list.iterator(), output); } /** * Serialize a sequence of Java objects into a YAML stream. * * @param data * Iterator with Objects * @param output * stream to write to */ @SuppressWarnings("deprecation") public void dumpAll(final Iterator<? extends Object> data, final Writer output) { dumpAll(data, output, dumperOptions.getExplicitRoot()); } private void dumpAll(final Iterator<? extends Object> data, final Writer output, final Tag rootTag) { Serializer serializer = new Serializer(new Emitter(output, dumperOptions), resolver, dumperOptions, rootTag); try { serializer.open(); while (data.hasNext()) { Node node = representer.represent(data.next()); serializer.serialize(node); } serializer.close(); } catch (java.io.IOException e) { throw new YAMLException(e); } } /** * <p> * Serialize a Java object into a YAML string. Override the default root tag with <code>rootTag</code>. * </p> * * <p> * This method is similar to <code>Yaml.dump(data)</code> except that the root tag for the whole document is replaced with the given * tag. This has two main uses. * </p> * * <p> * First, if the root tag is replaced with a standard YAML tag, such as <code>Tag.MAP</code>, then the object will be dumped as a map. * The root tag will appear as <code>!!map</code>, or blank (implicit !!map). * </p> * * <p> * Second, if the root tag is replaced by a different custom tag, then the document appears to be a different type when loaded. For * example, if an instance of MyClass is dumped with the tag !!YourClass, then it will be handled as an instance of YourClass when * loaded. * </p> * * @param data * Java object to be serialized to YAML * @param rootTag * the tag for the whole YAML document. The tag should be Tag.MAP for a JavaBean to make the tag disappear (to use implicit * tag !!map). If <code>null</code> is provided then the standard tag with the full class name is used. * @param flowStyle * flow style for the whole document. See Chapter 10. Collection Styles http://yaml.org/spec/1.1/#id930798. If * <code>null</code> is provided then the flow style from DumperOptions is used. * * @return YAML String */ public String dumpAs(final Object data, final Tag rootTag, final FlowStyle flowStyle) { FlowStyle oldStyle = representer.getDefaultFlowStyle(); if (flowStyle != null) { representer.setDefaultFlowStyle(flowStyle); } List<Object> list = new ArrayList<Object>(1); list.add(data); StringWriter buffer = new StringWriter(); dumpAll(list.iterator(), buffer, rootTag); representer.setDefaultFlowStyle(oldStyle); return buffer.toString(); } /** * <p> * Serialize a Java object into a YAML string. Override the default root tag with <code>Tag.MAP</code>. * </p> * <p> * This method is similar to <code>Yaml.dump(data)</code> except that the root tag for the whole document is replaced with * <code>Tag.MAP</code> tag (implicit !!map). * </p> * <p> * Block Mapping is used as the collection style. See 10.2.2. Block Mappings (http://yaml.org/spec/1.1/#id934537) * </p> * * @param data * Java object to be serialized to YAML * @return YAML String */ public String dumpAsMap(final Object data) { return dumpAs(data, Tag.MAP, FlowStyle.BLOCK); } /** * Serialize the representation tree into Events. * * @see <a href="http://yaml.org/spec/1.1/#id859333">Processing Overview</a> * @param data * representation tree * @return Event list */ public List<Event> serialize(final Node data) { SilentEmitter emitter = new SilentEmitter(); @SuppressWarnings("deprecation") Serializer serializer = new Serializer(emitter, resolver, dumperOptions, dumperOptions.getExplicitRoot()); try { serializer.open(); serializer.serialize(data); serializer.close(); } catch (java.io.IOException e) { throw new YAMLException(e); } return emitter.getEvents(); } private static class SilentEmitter implements Emitable { private List<Event> events = new ArrayList<Event>(100); public List<Event> getEvents() { return events; } @Override public void emit(final Event event) throws IOException { events.add(event); } } /** * Parse the only YAML document in a String and produce the corresponding Java object. (Because the encoding in known BOM is not * respected.) * * @param yaml * YAML data to load from (BOM must not be present) * @return parsed object */ public Object load(final String yaml) { return loadFromReader(new StreamReader(yaml), Object.class); } /** * Parse the only YAML document in a stream and produce the corresponding Java object. * * @param io * data to load from (BOM is respected and removed) * @return parsed object */ public Object load(final InputStream io) { return loadFromReader(new StreamReader(new UnicodeReader(io)), Object.class); } /** * Parse the only YAML document in a stream and produce the corresponding Java object. * * @param io * data to load from (BOM must not be present) * @return parsed object */ public Object load(final Reader io) { return loadFromReader(new StreamReader(io), Object.class); } /** * Parse the only YAML document in a stream and produce the corresponding Java object. * * @param <T> * Class is defined by the second argument * @param io * data to load from (BOM must not be present) * @param type * Class of the object to be created * @return parsed object */ @SuppressWarnings("unchecked") public <T> T loadAs(final Reader io, final Class<T> type) { return (T) loadFromReader(new StreamReader(io), type); } /** * Parse the only YAML document in a String and produce the corresponding Java object. (Because the encoding in known BOM is not * respected.) * * @param <T> * Class is defined by the second argument * @param yaml * YAML data to load from (BOM must not be present) * @param type * Class of the object to be created * @return parsed object */ @SuppressWarnings("unchecked") public <T> T loadAs(final String yaml, final Class<T> type) { return (T) loadFromReader(new StreamReader(yaml), type); } /** * Parse the only YAML document in a stream and produce the corresponding Java object. * * @param <T> * Class is defined by the second argument * @param input * data to load from (BOM is respected and removed) * @param type * Class of the object to be created * @return parsed object */ @SuppressWarnings("unchecked") public <T> T loadAs(final InputStream input, final Class<T> type) { return (T) loadFromReader(new StreamReader(new UnicodeReader(input)), type); } private Object loadFromReader(final StreamReader sreader, final Class<?> type) { Composer composer = new Composer(new ParserImpl(sreader), resolver); constructor.setComposer(composer); return constructor.getSingleData(type); } /** * Parse all YAML documents in a String and produce corresponding Java objects. The documents are parsed only when the iterator is * invoked. * * @param yaml * YAML data to load from (BOM must not be present) * @return an iterator over the parsed Java objects in this String in proper sequence */ public Iterable<Object> loadAll(final Reader yaml) { Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver); constructor.setComposer(composer); Iterator<Object> result = new Iterator<Object>() { @Override public boolean hasNext() { return constructor.checkData(); } @Override public Object next() { return constructor.getData(); } @Override public void remove() { throw new UnsupportedOperationException(); } }; return new YamlIterable(result); } private static class YamlIterable implements Iterable<Object> { private Iterator<Object> iterator; public YamlIterable(final Iterator<Object> iterator) { this.iterator = iterator; } @Override public Iterator<Object> iterator() { return iterator; } } /** * Parse all YAML documents in a String and produce corresponding Java objects. (Because the encoding in known BOM is not respected.) * The documents are parsed only when the iterator is invoked. * * @param yaml * YAML data to load from (BOM must not be present) * @return an iterator over the parsed Java objects in this String in proper sequence */ public Iterable<Object> loadAll(final String yaml) { return loadAll(new StringReader(yaml)); } /** * Parse all YAML documents in a stream and produce corresponding Java objects. The documents are parsed only when the iterator is * invoked. * * @param yaml * YAML data to load from (BOM is respected and ignored) * @return an iterator over the parsed Java objects in this stream in proper sequence */ public Iterable<Object> loadAll(final InputStream yaml) { return loadAll(new UnicodeReader(yaml)); } /** * Parse the first YAML document in a stream and produce the corresponding representation tree. (This is the opposite of the represent() * method) * * @see <a href="http://yaml.org/spec/1.1/#id859333">Figure 3.1. Processing Overview</a> * @param yaml * YAML document * @return parsed root Node for the specified YAML document */ public Node compose(final Reader yaml) { Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver); constructor.setComposer(composer); return composer.getSingleNode(); } /** * Parse all YAML documents in a stream and produce corresponding representation trees. * * @see <a href="http://yaml.org/spec/1.1/#id859333">Processing Overview</a> * @param yaml * stream of YAML documents * @return parsed root Nodes for all the specified YAML documents */ public Iterable<Node> composeAll(final Reader yaml) { final Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver); constructor.setComposer(composer); Iterator<Node> result = new Iterator<Node>() { @Override public boolean hasNext() { return composer.checkNode(); } @Override public Node next() { return composer.getNode(); } @Override public void remove() { throw new UnsupportedOperationException(); } }; return new NodeIterable(result); } private static class NodeIterable implements Iterable<Node> { private Iterator<Node> iterator; public NodeIterable(final Iterator<Node> iterator) { this.iterator = iterator; } @Override public Iterator<Node> iterator() { return iterator; } } /** * Add an implicit scalar detector. If an implicit scalar value matches the given regexp, the corresponding tag is assigned to the * scalar. * * @deprecated use Tag instead of String * @param tag * tag to assign to the node * @param regexp * regular expression to match against * @param first * a sequence of possible initial characters or null (which means any). * */ @Deprecated public void addImplicitResolver(final String tag, final Pattern regexp, final String first) { addImplicitResolver(new Tag(tag), regexp, first); } /** * Add an implicit scalar detector. If an implicit scalar value matches the given regexp, the corresponding tag is assigned to the * scalar. * * @param tag * tag to assign to the node * @param regexp * regular expression to match against * @param first * a sequence of possible initial characters or null (which means any). */ public void addImplicitResolver(final Tag tag, final Pattern regexp, final String first) { resolver.addImplicitResolver(tag, regexp, first); } @Override public String toString() { return name; } /** * Get a meaningful name. It simplifies debugging in a multi-threaded environment. If nothing is set explicitly the address of the * instance is returned. * * @return human readable name */ public String getName() { return name; } /** * Set a meaningful name to be shown in toString() * * @param name * human readable name */ public void setName(final String name) { this.name = name; } /** * Parse a YAML stream and produce parsing events. * * @see <a href="http://yaml.org/spec/1.1/#id859333">Processing Overview</a> * @param yaml * YAML document(s) * @return parsed events */ public Iterable<Event> parse(final Reader yaml) { final Parser parser = new ParserImpl(new StreamReader(yaml)); Iterator<Event> result = new Iterator<Event>() { @Override public boolean hasNext() { return parser.peekEvent() != null; } @Override public Event next() { return parser.getEvent(); } @Override public void remove() { throw new UnsupportedOperationException(); } }; return new EventIterable(result); } private static class EventIterable implements Iterable<Event> { private Iterator<Event> iterator; public EventIterable(final Iterator<Event> iterator) { this.iterator = iterator; } @Override public Iterator<Event> iterator() { return iterator; } } public void setBeanAccess(final BeanAccess beanAccess) { constructor.getPropertyUtils().setBeanAccess(beanAccess); representer.getPropertyUtils().setBeanAccess(beanAccess); } // deprecated /** * @deprecated use with Constructor instead of Loader */ @Deprecated public Yaml(final Loader loader) { this(loader, new Dumper(new DumperOptions())); } /** * @deprecated use with Constructor instead of Loader */ @Deprecated public Yaml(final Loader loader, final Dumper dumper) { this(loader, dumper, new Resolver()); } /** * @deprecated use with Constructor instead of Loader */ @Deprecated public Yaml(final Loader loader, final Dumper dumper, final Resolver resolver) { this(loader.constructor, dumper.representer, dumper.options, resolver); } /** * Create Yaml instance. It is safe to create a few instances and use them in different Threads. * * @param dumper * Dumper to emit outgoing objects */ @SuppressWarnings("deprecation") public Yaml(final Dumper dumper) { this(new Constructor(), dumper.representer, dumper.options); } }