|  | @@ -637,10 +637,10 @@ public abstract class FieldMapper extends Mapper implements Cloneable {
 | 
	
		
			
				|  |  |          private final TriFunction<String, MappingParserContext, Object, T> parser;
 | 
	
		
			
				|  |  |          private final Function<FieldMapper, T> initializer;
 | 
	
		
			
				|  |  |          private boolean acceptsNull = false;
 | 
	
		
			
				|  |  | -        private List<Consumer<T>> validators = new ArrayList<>();
 | 
	
		
			
				|  |  | -        private Serializer<T> serializer = XContentBuilder::field;
 | 
	
		
			
				|  |  | +        private final List<Consumer<T>> validators = new ArrayList<>();
 | 
	
		
			
				|  |  | +        private final Serializer<T> serializer;
 | 
	
		
			
				|  |  |          private SerializerCheck<T> serializerCheck = (includeDefaults, isConfigured, value) -> includeDefaults || isConfigured;
 | 
	
		
			
				|  |  | -        private Function<T, String> conflictSerializer = Objects::toString;
 | 
	
		
			
				|  |  | +        private final Function<T, String> conflictSerializer;
 | 
	
		
			
				|  |  |          private boolean deprecated;
 | 
	
		
			
				|  |  |          private MergeValidator<T> mergeValidator;
 | 
	
		
			
				|  |  |          private T value;
 | 
	
	
		
			
				|  | @@ -655,13 +655,20 @@ public abstract class FieldMapper extends Mapper implements Cloneable {
 | 
	
		
			
				|  |  |           * @param defaultValue  the default value for the parameter, used if unspecified in mappings
 | 
	
		
			
				|  |  |           * @param parser        a function that converts an object to a parameter value
 | 
	
		
			
				|  |  |           * @param initializer   a function that reads a parameter value from an existing mapper
 | 
	
		
			
				|  |  | +         * @param serializer    a function that serializes a parameter value, prefer type specific x-content generation methods here for
 | 
	
		
			
				|  |  | +         *                      good performance as this is used on the hot-path during cluster state updates.
 | 
	
		
			
				|  |  | +         *                      This should explicitly not be linked with {@link XContentBuilder#field(String, Object)} by callers through
 | 
	
		
			
				|  |  | +         *                      the use of default values or other indirection to this constructor.
 | 
	
		
			
				|  |  | +         * @param conflictSerializer a function that serializes a parameter value on conflict
 | 
	
		
			
				|  |  |           */
 | 
	
		
			
				|  |  |          public Parameter(
 | 
	
		
			
				|  |  |              String name,
 | 
	
		
			
				|  |  |              boolean updateable,
 | 
	
		
			
				|  |  |              Supplier<T> defaultValue,
 | 
	
		
			
				|  |  |              TriFunction<String, MappingParserContext, Object, T> parser,
 | 
	
		
			
				|  |  | -            Function<FieldMapper, T> initializer
 | 
	
		
			
				|  |  | +            Function<FieldMapper, T> initializer,
 | 
	
		
			
				|  |  | +            Serializer<T> serializer,
 | 
	
		
			
				|  |  | +            Function<T, String> conflictSerializer
 | 
	
		
			
				|  |  |          ) {
 | 
	
		
			
				|  |  |              this.name = name;
 | 
	
		
			
				|  |  |              this.defaultValue = Objects.requireNonNull(defaultValue);
 | 
	
	
		
			
				|  | @@ -669,6 +676,8 @@ public abstract class FieldMapper extends Mapper implements Cloneable {
 | 
	
		
			
				|  |  |              this.parser = parser;
 | 
	
		
			
				|  |  |              this.initializer = initializer;
 | 
	
		
			
				|  |  |              this.mergeValidator = (previous, toMerge, conflicts) -> updateable || Objects.equals(previous, toMerge);
 | 
	
		
			
				|  |  | +            this.serializer = serializer;
 | 
	
		
			
				|  |  | +            this.conflictSerializer = conflictSerializer;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /**
 | 
	
	
		
			
				|  | @@ -745,15 +754,6 @@ public abstract class FieldMapper extends Mapper implements Cloneable {
 | 
	
		
			
				|  |  |              return this;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /**
 | 
	
		
			
				|  |  | -         * Configure a custom serializer for this parameter
 | 
	
		
			
				|  |  | -         */
 | 
	
		
			
				|  |  | -        public Parameter<T> setSerializer(Serializer<T> serializer, Function<T, String> conflictSerializer) {
 | 
	
		
			
				|  |  | -            this.serializer = serializer;
 | 
	
		
			
				|  |  | -            this.conflictSerializer = conflictSerializer;
 | 
	
		
			
				|  |  | -            return this;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          /**
 | 
	
		
			
				|  |  |           * Configure a custom serialization check for this parameter
 | 
	
		
			
				|  |  |           */
 | 
	
	
		
			
				|  | @@ -859,7 +859,15 @@ public abstract class FieldMapper extends Mapper implements Cloneable {
 | 
	
		
			
				|  |  |              Function<FieldMapper, Boolean> initializer,
 | 
	
		
			
				|  |  |              boolean defaultValue
 | 
	
		
			
				|  |  |          ) {
 | 
	
		
			
				|  |  | -            return new Parameter<>(name, updateable, () -> defaultValue, (n, c, o) -> XContentMapValues.nodeBooleanValue(o), initializer);
 | 
	
		
			
				|  |  | +            return new Parameter<>(
 | 
	
		
			
				|  |  | +                name,
 | 
	
		
			
				|  |  | +                updateable,
 | 
	
		
			
				|  |  | +                () -> defaultValue,
 | 
	
		
			
				|  |  | +                (n, c, o) -> XContentMapValues.nodeBooleanValue(o),
 | 
	
		
			
				|  |  | +                initializer,
 | 
	
		
			
				|  |  | +                XContentBuilder::field,
 | 
	
		
			
				|  |  | +                Objects::toString
 | 
	
		
			
				|  |  | +            );
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /**
 | 
	
	
		
			
				|  | @@ -881,8 +889,10 @@ public abstract class FieldMapper extends Mapper implements Cloneable {
 | 
	
		
			
				|  |  |                  updateable,
 | 
	
		
			
				|  |  |                  defaultValue ? () -> Explicit.IMPLICIT_TRUE : () -> Explicit.IMPLICIT_FALSE,
 | 
	
		
			
				|  |  |                  (n, c, o) -> Explicit.explicitBoolean(XContentMapValues.nodeBooleanValue(o)),
 | 
	
		
			
				|  |  | -                initializer
 | 
	
		
			
				|  |  | -            ).setSerializer((b, n, v) -> b.field(n, v.value()), v -> Boolean.toString(v.value()));
 | 
	
		
			
				|  |  | +                initializer,
 | 
	
		
			
				|  |  | +                (b, n, v) -> b.field(n, v.value()),
 | 
	
		
			
				|  |  | +                v -> Boolean.toString(v.value())
 | 
	
		
			
				|  |  | +            );
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /**
 | 
	
	
		
			
				|  | @@ -898,7 +908,15 @@ public abstract class FieldMapper extends Mapper implements Cloneable {
 | 
	
		
			
				|  |  |              Function<FieldMapper, Integer> initializer,
 | 
	
		
			
				|  |  |              int defaultValue
 | 
	
		
			
				|  |  |          ) {
 | 
	
		
			
				|  |  | -            return new Parameter<>(name, updateable, () -> defaultValue, (n, c, o) -> XContentMapValues.nodeIntegerValue(o), initializer);
 | 
	
		
			
				|  |  | +            return new Parameter<>(
 | 
	
		
			
				|  |  | +                name,
 | 
	
		
			
				|  |  | +                updateable,
 | 
	
		
			
				|  |  | +                () -> defaultValue,
 | 
	
		
			
				|  |  | +                (n, c, o) -> XContentMapValues.nodeIntegerValue(o),
 | 
	
		
			
				|  |  | +                initializer,
 | 
	
		
			
				|  |  | +                XContentBuilder::field,
 | 
	
		
			
				|  |  | +                Objects::toString
 | 
	
		
			
				|  |  | +            );
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /**
 | 
	
	
		
			
				|  | @@ -914,7 +932,25 @@ public abstract class FieldMapper extends Mapper implements Cloneable {
 | 
	
		
			
				|  |  |              Function<FieldMapper, String> initializer,
 | 
	
		
			
				|  |  |              String defaultValue
 | 
	
		
			
				|  |  |          ) {
 | 
	
		
			
				|  |  | -            return new Parameter<>(name, updateable, () -> defaultValue, (n, c, o) -> XContentMapValues.nodeStringValue(o), initializer);
 | 
	
		
			
				|  |  | +            return stringParam(name, updateable, initializer, defaultValue, XContentBuilder::field);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public static Parameter<String> stringParam(
 | 
	
		
			
				|  |  | +            String name,
 | 
	
		
			
				|  |  | +            boolean updateable,
 | 
	
		
			
				|  |  | +            Function<FieldMapper, String> initializer,
 | 
	
		
			
				|  |  | +            String defaultValue,
 | 
	
		
			
				|  |  | +            Serializer<String> serializer
 | 
	
		
			
				|  |  | +        ) {
 | 
	
		
			
				|  |  | +            return new Parameter<>(
 | 
	
		
			
				|  |  | +                name,
 | 
	
		
			
				|  |  | +                updateable,
 | 
	
		
			
				|  |  | +                () -> defaultValue,
 | 
	
		
			
				|  |  | +                (n, c, o) -> XContentMapValues.nodeStringValue(o),
 | 
	
		
			
				|  |  | +                initializer,
 | 
	
		
			
				|  |  | +                serializer,
 | 
	
		
			
				|  |  | +                Function.identity()
 | 
	
		
			
				|  |  | +            );
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          @SuppressWarnings("unchecked")
 | 
	
	
		
			
				|  | @@ -931,7 +967,7 @@ public abstract class FieldMapper extends Mapper implements Cloneable {
 | 
	
		
			
				|  |  |                      strValues.add(item.toString());
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |                  return strValues;
 | 
	
		
			
				|  |  | -            }, initializer);
 | 
	
		
			
				|  |  | +            }, initializer, XContentBuilder::stringListField, Objects::toString);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /**
 | 
	
	
		
			
				|  | @@ -1010,7 +1046,7 @@ public abstract class FieldMapper extends Mapper implements Cloneable {
 | 
	
		
			
				|  |  |                  } catch (IllegalArgumentException e) {
 | 
	
		
			
				|  |  |                      throw new MapperParsingException("Unknown value [" + o + "] for field [" + name + "] - accepted values are " + values);
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | -            }, initializer).addValidator(v -> {
 | 
	
		
			
				|  |  | +            }, initializer, XContentBuilder::field, Objects::toString).addValidator(v -> {
 | 
	
		
			
				|  |  |                  if (v != null && values.contains(v) == false) {
 | 
	
		
			
				|  |  |                      throw new MapperParsingException("Unknown value [" + v + "] for field [" + name + "] - accepted values are " + values);
 | 
	
		
			
				|  |  |                  }
 | 
	
	
		
			
				|  | @@ -1037,7 +1073,7 @@ public abstract class FieldMapper extends Mapper implements Cloneable {
 | 
	
		
			
				|  |  |                      throw new IllegalArgumentException("analyzer [" + analyzerName + "] has not been configured in mappings");
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |                  return a;
 | 
	
		
			
				|  |  | -            }, initializer).setSerializer((b, n, v) -> b.field(n, v.name()), NamedAnalyzer::name);
 | 
	
		
			
				|  |  | +            }, initializer, (b, n, v) -> b.field(n, v.name()), NamedAnalyzer::name);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /**
 | 
	
	
		
			
				|  | @@ -1049,7 +1085,9 @@ public abstract class FieldMapper extends Mapper implements Cloneable {
 | 
	
		
			
				|  |  |                  true,
 | 
	
		
			
				|  |  |                  Collections::emptyMap,
 | 
	
		
			
				|  |  |                  (n, c, o) -> TypeParsers.parseMeta(n, o),
 | 
	
		
			
				|  |  | -                m -> m.fieldType().meta()
 | 
	
		
			
				|  |  | +                m -> m.fieldType().meta(),
 | 
	
		
			
				|  |  | +                XContentBuilder::stringStringMap,
 | 
	
		
			
				|  |  | +                Objects::toString
 | 
	
		
			
				|  |  |              );
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1080,7 +1118,7 @@ public abstract class FieldMapper extends Mapper implements Cloneable {
 | 
	
		
			
				|  |  |                      throw new IllegalArgumentException("stored scripts are not supported on field [" + n + "]");
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |                  return script;
 | 
	
		
			
				|  |  | -            }, initializer).acceptsNull();
 | 
	
		
			
				|  |  | +            }, initializer, XContentBuilder::field, Objects::toString).acceptsNull();
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /**
 |