Весна, чтобы понять свойства в YAML - PullRequest
9 голосов
/ 01 апреля 2010

Spring отказался от использования YAML в качестве альтернативы .properties / .xml из-за:

[Spring Developer]: ... Рассматривался YAML, но мы думали, что подсчет значительных пробелов при создании был кошмаром поддержки ... [ссылка весенний форум ]

Я уверен, что YAML имеет много смысла для свойств, и я использую его в настоящее время в проекте, но у меня есть трудности с введением свойств в

<property name="productName" value="${client.product.name}" />

мода.

Что-то, что мне не хватает, или я должен создать пользовательский YamlPropertyPlaceholderConfigurer?

Ответы [ 4 ]

9 голосов
/ 03 февраля 2011

Я не знаю, не слишком ли поздно, но нет - вам не нужно реализовывать весь YamlPropertyPlaceholderConfigurer, вместо этого вы можете просто создать собственный PropertiesPersister и добавить его как необязательный параметр.

Вот как будет выглядеть ваша конфигурация

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <value>file:///C:/somewhere/site.yaml</value>
    </property>
    <property name="propertiesPersister" ref="persister"></property>
</bean>
<bean id="persister" class="com.foo.utils.YamlPropertiesPersister"></bean>

А вот голая (только для чтения) реализация, использующая SnakeYaml, не стесняйтесь добавлять то, что вам нужно, включая обработку ошибок

public class YamlPropertiesPersister implements PropertiesPersister {
@Override
public void load(Properties props, InputStream is) throws IOException {
    load(props, new InputStreamReader(is));
}

/**
 * We want to traverse map representing Yaml object and each time we find String=String pair we want to
 * save it as Property. As we are going deeper into map we generate compound key as path-like String
 * 
 * @param props
 * @param reader
 * @throws IOException
 * @see org.springframework.util.PropertiesPersister#load(java.util.Properties, java.io.Reader)
 */
@Override
public void load(Properties props, Reader reader) throws IOException {
    Yaml yaml = CollectorUtils.instanceOfYaml();
    Map<String, Object> map = (Map<String, Object>) yaml.load(reader);
    // now we can populate supplied props
    assignProperties(props, map, null);
}

/**
 * @param props
 * @param map
 */
public void assignProperties(Properties props, Map<String, Object> map, String path) {
    for (Entry<String, Object> entry : map.entrySet()) {
        String key = entry.getKey();
        if (StringUtils.isNotEmpty(path))
            key = path + "." + key;
        Object val = entry.getValue();
        if (val instanceof String) {
            // see if we need to create a compound key
            props.put(key, val);
        } else if (val instanceof Map) {
            assignProperties(props, (Map<String, Object>) val, key);
        }
    }
}

@Override
public void store(Properties props, OutputStream os, String header) throws IOException {
    throw new NotImplementedException("Current implementation is a read-only");
}

@Override
public void store(Properties props, Writer writer, String header) throws IOException {
    throw new NotImplementedException("Current implementation is a read-only");
}

@Override
public void loadFromXml(Properties props, InputStream is) throws IOException {
    throw new NotImplementedException("Use DefaultPropertiesPersister if you want to read/write XML");
}

@Override
public void storeToXml(Properties props, OutputStream os, String header) throws IOException {
    throw new NotImplementedException("Use DefaultPropertiesPersister if you want to load/store to XML");
}

@Override
public void storeToXml(Properties props, OutputStream os, String header, String encoding) throws IOException {
    throw new NotImplementedException("Use DefaultPropertiesPersister if you want to read/write XML");
}
}

Как дополнительная выгода - вот как я создаю экземпляр Yaml

    public static Yaml instanceOfYaml() {
    DumperOptions options = new DumperOptions();
    options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
    options.setDefaultScalarStyle(ScalarStyle.DOUBLE_QUOTED);
    final Yaml yaml = new Yaml(new Loader(), new Dumper(options), new Resolver() {
        /**
         * @see org.yaml.snakeyaml.resolver.Resolver#addImplicitResolvers()
         */
        @Override
        protected void addImplicitResolvers() {
            addImplicitResolver(Tag.BOOL, BOOL, "yYnNtTfFoO");
            // disable resolving of floats and integers
            // addImplicitResolver(Tags.FLOAT, FLOAT, "-+0123456789.");
            // addImplicitResolver(Tag.INT, INT, "-+0123456789");
            addImplicitResolver(Tag.MERGE, MERGE, "<");
            addImplicitResolver(Tag.NULL, NULL, "~nN\0");
            addImplicitResolver(Tag.NULL, EMPTY, null);
            addImplicitResolver(Tag.TIMESTAMP, TIMESTAMP, "0123456789");
            addImplicitResolver(Tag.VALUE, VALUE, "=");
        }
    });
    return yaml;
}

Вы также можете прочитать это в моем блоге

3 голосов
/ 20 ноября 2012

Для тех, кто использует Spring 3.1, вы можете зарегистрировать Yaml PropetySource.Код SnakeYaml взят из кода Бостона (спасибо), адаптированного к новой системе PropertySource в Spring 3.1.

import com.google.common.base.Preconditions;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;
import org.yaml.snakeyaml.Dumper;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
import org.yaml.snakeyaml.Loader;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.resolver.Resolver;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

/**
 * @author Sebastien Lorber <i>(lorber.sebastien@gmail.com)</i>
 */
public class YamlPropertiesSource extends PropertiesPropertySource {


  public YamlPropertiesSource(String name, Resource yamlResource) {
    super(name, getPropertySource(yamlResource) );
  }

  private static Properties getPropertySource(Resource yamlResource) {
    Preconditions.checkArgument(yamlResource != null,"no yaml resource provided");
    try {
      InputStream is = yamlResource.getInputStream();
      Properties properties = new Properties();
      load(properties, is);
      return properties;
    } catch ( Exception e ) {
      throw new IllegalStateException("Can't get PropertySource from YAML resource=" + yamlResource,e);
    }
  }

  private static void load(Properties props, InputStream is) throws IOException {
    load(props, new InputStreamReader(is));
  }

  private static void load(Properties props, Reader reader) throws IOException {
    Yaml yaml = instanceOfYaml();
    @SuppressWarnings("unchecked")
    Map<String, Object> map = (Map<String, Object>) yaml.load(reader);
    // now we can populate supplied props
    assignProperties(props, map, null);
  }

  private static void assignProperties(Properties props, Map<String, Object> map, String path) {
    for (Entry<String, Object> entry : map.entrySet()) {
      String key = entry.getKey();
      if ( StringUtils.hasLength(path) )
        key = path + "." + key;
      Object val = entry.getValue();
      if (val instanceof String) {
        // see if we need to create a compound key
        props.put(key, val);
      } else if (val instanceof Map) {
        assignProperties(props, (Map<String, Object>) val, key);
      }
    }
  }

  public static Yaml instanceOfYaml() {
    DumperOptions options = new DumperOptions();
    options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
    options.setDefaultScalarStyle(ScalarStyle.DOUBLE_QUOTED);
    final Yaml yaml = new Yaml(new Loader(), new Dumper(options), new Resolver() {
      /**
       * @see org.yaml.snakeyaml.resolver.Resolver#addImplicitResolvers()
       */
      @Override
      protected void addImplicitResolvers() {
        addImplicitResolver(Tag.BOOL, BOOL, "yYnNtTfFoO");
        // disable resolving of floats and integers
        // addImplicitResolver(Tags.FLOAT, FLOAT, "-+0123456789.");
        // addImplicitResolver(Tag.INT, INT, "-+0123456789");
        addImplicitResolver(Tag.MERGE, MERGE, "<");
        addImplicitResolver(Tag.NULL, NULL, "~nN\0");
        addImplicitResolver(Tag.NULL, EMPTY, null);
        addImplicitResolver(Tag.TIMESTAMP, TIMESTAMP, "0123456789");
        addImplicitResolver(Tag.VALUE, VALUE, "=");
      }
    });
    return yaml;
  }

}

Обратите внимание, что это также основано на ResourcePropertySource и загружает свойства в кодировку ISO 8859-1.Я открыл для этого ошибку: SPR-10096

Вы можете добавить источник этого свойства в контекст своего приложения.Это также можно сделать в ваших юнит-тестах:

public class PropertySourceContextLoader extends GenericXmlContextLoader {
    @Override
    protected void loadBeanDefinitions(GenericApplicationContext context,MergedContextConfiguration mergedConfig) {
        PropertySource<String> ps = new MyPropertySource();
        context.getEnvironment().getPropertySources().addLast(ps);
        super.loadBeanDefinitions(context, mergedConfig);
    }
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = PropertySourceContextLoader.class, locations = { "classpath:/spring-application-context.xml" })
public class SpringBasedTest {
         ..........
}
1 голос
/ 22 апреля 2014

Для полных и совершенных кусочков, как я, которые практически не знают, что на самом деле делает автор, но все равно должны это делать ... вот как я это сделал. Понятия не имею, как отменить устаревание instanceOfYaml (). Еще одна вещь, мой проект Spring Boot Eclipse, читаемый из файлов, помеченных как .yml, а не .yaml

import org.springframework.util.PropertiesPersister;
import org.springframework.util.StringUtils;
import org.yaml.snakeyaml.Dumper;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
import org.yaml.snakeyaml.Loader;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.resolver.Resolver;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

public class YamlPropertiesPersister implements PropertiesPersister {
    @Override
    public void load(Properties props, InputStream is) throws IOException {
        load(props, new InputStreamReader(is));
    }

    /**
     * We want to traverse map representing Yaml object and each time we will find String:String value pair we want to
     * save it as Property. As we are going deeper into map we generate a compound key as path-like String
     *
     * @param props
     * @param reader
     * @throws IOException
     * @see org.springframework.util.PropertiesPersister#load(java.util.Properties, java.io.Reader)
     */
    @Override
    public void load(Properties props, Reader reader) throws IOException {
        Yaml yaml = instanceOfYaml();
        Map<String, Object> map = (Map<String, Object>) yaml.load(reader);
        // now we can populate supplied props
        assignProperties(props, map, null);
    }

    /**
     * @param props
     * @param map
     */
    public void assignProperties(Properties props, Map<String, Object> map, String path) {
        for (Entry<String, Object> entry : map.entrySet()) {
            String key = entry.getKey();
            if (!StringUtils.isEmpty(path))
                key = path + "." + key;
            Object val = entry.getValue();
            if (val instanceof String) {
                // see if we need to create a compound key
                props.put(key, val);
            } else if (val instanceof Map) {
                assignProperties(props, (Map<String, Object>) val, key);
            }
        }
    }

    @Override
    public void store(Properties props, OutputStream os, String header) throws IOException {
        throw new UnsupportedOperationException("Current implementation is a read-only");
    }

    @Override
    public void store(Properties props, Writer writer, String header) throws IOException {
        throw new UnsupportedOperationException("Current implementation is a read-only");
    }

    @Override
    public void loadFromXml(Properties props, InputStream is) throws IOException {
        throw new UnsupportedOperationException("Use DefaultPropertiesPersister if you want to read/write XML");
    }

    @Override
    public void storeToXml(Properties props, OutputStream os, String header) throws IOException {
        throw new UnsupportedOperationException("Use DefaultPropertiesPersister if you want to load/store to XML");
    }

    @Override
    public void storeToXml(Properties props, OutputStream os, String header, String encoding) throws IOException {
        throw new UnsupportedOperationException("Use DefaultPropertiesPersister if you want to read/write XML");
    }


    public static Yaml instanceOfYaml() {
        DumperOptions options = new DumperOptions();
        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
        options.setDefaultScalarStyle(ScalarStyle.DOUBLE_QUOTED);
        final Yaml yaml = new Yaml(new Loader(), new Dumper(options), new Resolver() {
            /**
             * @see org.yaml.snakeyaml.resolver.Resolver#addImplicitResolvers()
             */
            @Override
            protected void addImplicitResolvers() {
                addImplicitResolver(Tag.BOOL, BOOL, "yYnNtTfFoO");
                // disable resolving of floats and integers
                // addImplicitResolver(Tags.FLOAT, FLOAT, "-+0123456789.");
                // addImplicitResolver(Tag.INT, INT, "-+0123456789");
                addImplicitResolver(Tag.MERGE, MERGE, "<");
                addImplicitResolver(Tag.NULL, NULL, "~nN\0");
                addImplicitResolver(Tag.NULL, EMPTY, null);
                addImplicitResolver(Tag.TIMESTAMP, TIMESTAMP, "0123456789");
                addImplicitResolver(Tag.VALUE, VALUE, "=");
            }
        });
        return yaml;
    }
}
0 голосов
/ 02 апреля 2010

Сообщение на форуме, на которое вы ссылаетесь, было от форума dmServer, а не от Spring Framework, и между ними очень небольшая связь, поэтому я не стал бы ничего в него читать.

Кроме того, YAML практически не слышен в мире Java, поэтому добавление поддержки для него было бы в лучшем случае жестом токена (если вы простите выражение). XML преобладает в Java, особенно на стороне сервера, поэтому здесь нет смысла плавать против течения, особенно для такого меньшинства, как YAML.

Сказав это, написание собственного YamlPropertyPlaceholderConfigurer должно быть достаточно простым, при условии, что вы можете найти парсер YAML для Java.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...