private void parseProperties(
        ObjectMapper.Builder objBuilder,
        Map<String, Object> propsNode,
        ParserContext parserContext) {
      for (Map.Entry<String, Object> entry : propsNode.entrySet()) {
        String propName = entry.getKey();
        Map<String, Object> propNode = (Map<String, Object>) entry.getValue();

        String type;
        Object typeNode = propNode.get("type");
        if (typeNode != null) {
          type = typeNode.toString();
        } else {
          // lets see if we can derive this...
          if (propNode.get("properties") != null) {
            type = ObjectMapper.CONTENT_TYPE;
          } else if (propNode.get("fields") != null) {
            type = MultiFieldMapper.CONTENT_TYPE;
          } else {
            throw new MapperParsingException("No type specified for property [" + propName + "]");
          }
        }

        Mapper.TypeParser typeParser = parserContext.typeParser(type);
        if (typeParser == null) {
          throw new MapperParsingException(
              "No handler for type [" + type + "] declared on field [" + propName + "]");
        }
        objBuilder.add(typeParser.parse(propName, propNode, parserContext));
      }
    }
    @Override
    public Mapper.Builder parse(String name, Map<String, Object> node, ParserContext parserContext)
        throws MapperParsingException {
      Map<String, Object> objectNode = node;
      ObjectMapper.Builder builder = createBuilder(name);

      boolean nested = false;
      boolean nestedIncludeInParent = false;
      boolean nestedIncludeInRoot = false;
      for (Map.Entry<String, Object> entry : objectNode.entrySet()) {
        String fieldName = Strings.toUnderscoreCase(entry.getKey());
        Object fieldNode = entry.getValue();

        if (fieldName.equals("dynamic")) {
          String value = fieldNode.toString();
          if (value.equalsIgnoreCase("strict")) {
            builder.dynamic(Dynamic.STRICT);
          } else {
            builder.dynamic(nodeBooleanValue(fieldNode) ? Dynamic.TRUE : Dynamic.FALSE);
          }
        } else if (fieldName.equals("type")) {
          String type = fieldNode.toString();
          if (type.equals(CONTENT_TYPE)) {
            builder.nested = Nested.NO;
          } else if (type.equals(NESTED_CONTENT_TYPE)) {
            nested = true;
          } else {
            throw new MapperParsingException(
                "Trying to parse an object but has a different type ["
                    + type
                    + "] for ["
                    + name
                    + "]");
          }
        } else if (fieldName.equals("include_in_parent")) {
          nestedIncludeInParent = nodeBooleanValue(fieldNode);
        } else if (fieldName.equals("include_in_root")) {
          nestedIncludeInRoot = nodeBooleanValue(fieldNode);
        } else if (fieldName.equals("enabled")) {
          builder.enabled(nodeBooleanValue(fieldNode));
        } else if (fieldName.equals("path")) {
          builder.pathType(parsePathType(name, fieldNode.toString()));
        } else if (fieldName.equals("properties")) {
          parseProperties(builder, (Map<String, Object>) fieldNode, parserContext);
        } else if (fieldName.equals("include_in_all")) {
          builder.includeInAll(nodeBooleanValue(fieldNode));
        } else {
          processField(builder, fieldName, fieldNode);
        }
      }

      if (nested) {
        builder.nested = Nested.newNested(nestedIncludeInParent, nestedIncludeInRoot);
      }

      return builder;
    }