java Spring 理解 YAML 中的属性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2561947/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 21:48:27  来源:igfitidea点击:

Spring to understand properties in YAML

javaspringyaml

提问by tolitius

Did Spring abandon YAML to use as an alternative to .properties / .xml because of:

Spring 是否放弃了 YAML 作为 .properties / .xml 的替代品,因为:

[Spring Developer]: ...YAML was considered, but we thought that counting whitespace significant was a support nightmare in the making...[reference from spring forum]

[Spring 开发人员]:...YAML 被考虑过,但我们认为计算空格重要是一个正在酝酿中的支持噩梦...[来自spring 论坛的参考]

I am confident YAML makes a lot of sense for properties, and I am using it currently on the project, but have difficulties to inject properties in a

我相信 YAML 对属性很有意义,我目前正在项目中使用它,但是在将属性注入到一个

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

fashion.

时尚。

Anything I am missing, or I should create a custom YamlPropertyPlaceholderConfigurer ?

我缺少什么,或者我应该创建一个自定义 YamlPropertyPlaceholderConfigurer ?

采纳答案by Bostone

I don't know if this is a bit too late but no - you don't have to implement whole YamlPropertyPlaceholderConfigurer instead you can simply create custom PropertiesPersister and add it as optional parameter.

我不知道这是否为时已晚,但不是 - 您不必实现整个 YamlPropertyPlaceholderConfigurer 而是可以简单地创建自定义 PropertiesPersister 并将其添加为可选参数。

Here's how your configuration will look

这是您的配置的外观

<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>

And here's bare-bone (read-only) implementation using SnakeYaml, feel free to add what you need including error handling

这是使用 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");
}
}

As added benefit - here's how I create Yaml instance

作为额外的好处 - 这是我创建 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
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>([email protected])</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
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 {
         ..........
}
"); addImplicitResolver(Tag.NULL, EMPTY, null); addImplicitResolver(Tag.TIMESTAMP, TIMESTAMP, "0123456789"); addImplicitResolver(Tag.VALUE, VALUE, "="); } }); return yaml; } }
"); addImplicitResolver(Tag.NULL, EMPTY, null); addImplicitResolver(Tag.TIMESTAMP, TIMESTAMP, "0123456789"); addImplicitResolver(Tag.VALUE, VALUE, "="); } }); return yaml; }

You can also read this in my blog

您也可以在我的博客中阅读此内容

回答by Sebastien Lorber

For those using Spring 3.1, you can register a Yaml PropetySource. The SnakeYaml code is from the Bostone code (thanks) adapted to new PropertySource system of Spring 3.1.

对于使用 Spring 3.1 的用户,您可以注册一个 Yaml 属性源。SnakeYaml 代码来自适用于 Spring 3.1 新 PropertySource 系统的 Bostone 代码(感谢)。

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##代码##");
                addImplicitResolver(Tag.NULL, EMPTY, null);
                addImplicitResolver(Tag.TIMESTAMP, TIMESTAMP, "0123456789");
                addImplicitResolver(Tag.VALUE, VALUE, "=");
            }
        });
        return yaml;
    }
}

Please notice that this is also inspired by ResourcePropertySource, and it loads properties in ISO 8859-1 charset. I opened a bug for that: SPR-10096

请注意,这也受到 ResourcePropertySource 的启发,它以 ISO 8859-1 字符集加载属性。我为此打开了一个错误:SPR-10096

You can add this property source to your application context. This can also be done in your unit tests:

您可以将此属性源添加到您的应用程序上下文中。这也可以在您的单元测试中完成:

##代码##

回答by kickpuncher11

For complete and utter nubs like me who have zero knowledge of what the author is actually doing, but need to do it anyway...here's how I made it work. Have no idea how to de-deprecate the instanceOfYaml() though. One other thing, my Spring Boot Eclipse project read from files marked .yml, not .yaml

对于像我这样对作者实际在做什么完全零知识但无论如何都需要做的人来说……这就是我让它工作的方式。虽然不知道如何弃用 instanceOfYaml() 。另一件事,我的 Spring Boot Eclipse 项目从标记为 .yml 而不是 .yaml 的文件中读取

##代码##

回答by skaffman

The forum post you reference was from the dmServer forum, rather than the Spring Framework, and there's very little relation between the two, so I wouldn't read anything into it.

您引用的论坛帖子来自dmServer论坛,而不是Spring Framework,两者之间的关系很小,所以我不会阅读任何内容。

On top of that, YAML is pretty much unheard of in the Java world, so adding support for it would have been a token gesture (if you'll pardon the expression) at best. XML dominates in Java, especially server-side, so there's little use in swimming against the tide there, especially for a minority format like YAML.

最重要的是,YAML 在 Java 世界中几乎闻所未闻,因此添加对它的支持充其量只是一种象征性的姿态(如果你能原谅这个表达)。XML 在 Java 中占主导地位,尤其是在服务器端,因此在那里逆流而上几乎没有用,特别是对于像 YAML 这样的少数格式。

Having said that, writing your own YamlPropertyPlaceholderConfigurershould be easy enough, assuming you can find a YAML parser for Java.

话虽如此,编写自己的代码YamlPropertyPlaceholderConfigurer应该很容易,假设您可以找到适用于 Java 的 YAML 解析器。