in tensorflow_serving/util/json_tensor.cc [462:550]
Status FillTensorMapFromInstancesList(
const rapidjson::Value::MemberIterator& itr,
const ::google::protobuf::Map<string, tensorflow::TensorInfo>& tensorinfo_map,
::google::protobuf::Map<string, TensorProto>* tensor_map) {
// "instances" array can either be a plain list or list of objects (for named
// tensors) but not a mix of both. Each object must have one key for each
// named tensor.
if (!itr->value[0].IsObject() && tensorinfo_map.size() > 1) {
return errors::InvalidArgument(
"instances is a plain list, but expecting list of objects as multiple "
"input tensors required as per tensorinfo_map");
}
auto IsElementObject = [](const rapidjson::Value& val) {
return val.IsObject() && !IsValBase64Object(val);
};
const bool elements_are_objects = IsElementObject(itr->value[0]);
std::set<string> input_names;
for (const auto& kv : tensorinfo_map) input_names.insert(kv.first);
// Add each element of "instances" array to tensor.
//
// Each element must yield one tensor of same shape and size. All elements get
// batched into one tensor with the first dimension equal to the number of
// elements in the instances array.
tensor_map->clear();
::google::protobuf::Map<string, int> size_map;
::google::protobuf::Map<string, TensorShapeProto> shape_map;
int tensor_count = 0;
for (const auto& elem : itr->value.GetArray()) {
if (elements_are_objects) {
if (!IsElementObject(elem)) {
return errors::InvalidArgument("Expecting object but got list at item ",
tensor_count, " of input list");
}
std::set<string> object_keys;
for (const auto& kv : elem.GetObject()) {
const string& name = kv.name.GetString();
object_keys.insert(name);
const auto status = AddInstanceItem(kv.value, name, tensorinfo_map,
&size_map, &shape_map, tensor_map);
if (!status.ok()) {
return errors::InvalidArgument(
"Failed to process element: ", tensor_count, " key: ", name,
" of 'instances' list. Error: ", status.ToString());
}
}
if (input_names != object_keys) {
return errors::InvalidArgument(
"Failed to process element: ", tensor_count,
" of 'instances' list. JSON object: ", JsonValueToString(elem),
" keys must be equal to: ", absl::StrJoin(input_names, ","));
}
} else {
if (IsElementObject(elem)) {
return errors::InvalidArgument(
"Expecting value/list but got object at item ", tensor_count,
" of input list");
}
const auto& name = tensorinfo_map.begin()->first;
const auto status = AddInstanceItem(elem, name, tensorinfo_map, &size_map,
&shape_map, tensor_map);
if (!status.ok()) {
return errors::InvalidArgument(
"Failed to process element: ", tensor_count,
" of 'instances' list. Error: ", status.ToString());
}
}
tensor_count++;
}
// Now that all individual tensors from "instances" array are added,
// fix the resulting shape of the final tensor.
for (auto& kv : *tensor_map) {
const string& name = kv.first;
auto* tensor = &kv.second;
tensor->set_dtype(tensorinfo_map.at(name).dtype());
const auto& shape = shape_map.at(name);
auto* output_shape = tensor->mutable_tensor_shape();
output_shape->Clear();
output_shape->add_dim()->set_size(tensor_count);
for (const auto& d : shape.dim())
output_shape->add_dim()->set_size(d.size());
}
return Status::OK();
}