static bool ParseMaterial(Material *material, std::string *err, const json &o,
                          bool store_original_json_for_extras_and_extensions) {
  ParseStringProperty(&material->name, err, o, "name", /* required */ false);

  if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
                               "emissiveFactor",
                               /* required */ false)) {
    if (material->emissiveFactor.size() != 3) {
      if (err) {
        (*err) +=
            "Array length of `emissiveFactor` parameter in "
            "material must be 3, but got " +
            std::to_string(material->emissiveFactor.size()) + "\n";
      }
      return false;
    }
  } else {
    // fill with default values
    material->emissiveFactor = {0.0, 0.0, 0.0};
  }

  ParseStringProperty(&material->alphaMode, err, o, "alphaMode",
                      /* required */ false);
  ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff",
                      /* required */ false);
  ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided",
                       /* required */ false);

  {
    json_const_iterator it;
    if (FindMember(o, "pbrMetallicRoughness", it)) {
      ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
                                GetValue(it),
                                store_original_json_for_extras_and_extensions);
    }
  }

  {
    json_const_iterator it;
    if (FindMember(o, "normalTexture", it)) {
      ParseNormalTextureInfo(&material->normalTexture, err, GetValue(it),
                             store_original_json_for_extras_and_extensions);
    }
  }

  {
    json_const_iterator it;
    if (FindMember(o, "occlusionTexture", it)) {
      ParseOcclusionTextureInfo(&material->occlusionTexture, err, GetValue(it),
                                store_original_json_for_extras_and_extensions);
    }
  }

  {
    json_const_iterator it;
    if (FindMember(o, "emissiveTexture", it)) {
      ParseTextureInfo(&material->emissiveTexture, err, GetValue(it),
                       store_original_json_for_extras_and_extensions);
    }
  }

  // Old code path. For backward compatibility, we still store material values
  // as Parameter. This will create duplicated information for
  // example(pbrMetallicRoughness), but should be neglible in terms of memory
  // consumption.
  // TODO(syoyo): Remove in the next major release.
  material->values.clear();
  material->additionalValues.clear();

  json_const_iterator it(ObjectBegin(o));
  json_const_iterator itEnd(ObjectEnd(o));

  for (; it != itEnd; ++it) {
    std::string key(GetKey(it));
    if (key == "pbrMetallicRoughness") {
      if (IsObject(GetValue(it))) {
        const json &values_object = GetValue(it);

        json_const_iterator itVal(ObjectBegin(values_object));
        json_const_iterator itValEnd(ObjectEnd(values_object));

        for (; itVal != itValEnd; ++itVal) {
          Parameter param;
          if (ParseParameterProperty(&param, err, values_object, GetKey(itVal),
                                     false)) {
            material->values.emplace(GetKey(itVal), std::move(param));
          }
        }
      }
    } else if (key == "extensions" || key == "extras") {
      // done later, skip, otherwise poorly parsed contents will be saved in the
      // parametermap and serialized again later
    } else {
      Parameter param;
      if (ParseParameterProperty(&param, err, o, key, false)) {
        // names of materials have already been parsed. Putting it in this map
        // doesn't correctly reflext the glTF specification
        if (key != "name")
          material->additionalValues.emplace(std::move(key), std::move(param));
      }
    }
  }

  material->extensions.clear();
  ParseExtensionsProperty(&material->extensions, err, o);
  ParseExtrasProperty(&(material->extras), o);

  if (store_original_json_for_extras_and_extensions) {
    {
      json_const_iterator eit;
      if (FindMember(o, "extensions", eit)) {
        material->extensions_json_string = JsonToString(GetValue(eit));
      }
    }
    {
      json_const_iterator eit;
      if (FindMember(o, "extras", eit)) {
        material->extras_json_string = JsonToString(GetValue(eit));
      }
    }
  }

  return true;
}