Container fixes for the Json Serialization

These changes fix the following:
- Containers treat new values as new objects and make sure they're initialized.
- Fixed sized containers behave slightly different and will initialize all values when a new fixed sized container is created.
- Loading any values to a container will now return PartialDefaults instead of defaults used as adding any value to a container no longer makes the container a default as the default is always an empty container.
- The previous doesn't apply to fixed sized containers as those containers are always considered to have the exact number of values they can hold.
This commit is contained in:
AMZN-koppersr
2021-06-15 13:52:16 -07:00
parent f00fa26e12
commit 780dd0df9f
6 changed files with 165 additions and 65 deletions
@@ -32,13 +32,24 @@ namespace AZ
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
return LoadContainer(outputValue, outputValueTypeId, inputValue, context);
return LoadContainer(outputValue, outputValueTypeId, inputValue, false, context);
case rapidjson::kObjectType: // fall through
case rapidjson::kNullType: // fall through
case rapidjson::kStringType: // fall through
case rapidjson::kFalseType: // fall through
case rapidjson::kTrueType: // fall through
case rapidjson::kObjectType:
if (IsExplicitDefault(inputValue))
{
// Because this serializer has only the operation flag "InitializeNewInstance" set, the only time this will be called with
// an explicit default is when a new instance has been created.
return LoadContainer(outputValue, outputValueTypeId, inputValue, true, context);
}
[[fallthrough]];
case rapidjson::kNullType:
[[fallthrough]];
case rapidjson::kStringType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
[[fallthrough]];
case rapidjson::kNumberType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. AZStd::array entries can only be read from an array.");
@@ -129,7 +140,16 @@ namespace AZ
}
}
JsonSerializationResult::Result JsonArraySerializer::LoadContainer(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
auto JsonArraySerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
JsonSerializationResult::Result JsonArraySerializer::LoadContainer(
void* outputValue,
const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue,
bool isNewInstance,
JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used to remove name conflicts in AzCore in uber builds.
@@ -154,14 +174,7 @@ namespace AZ
"Unable to retrieve the correct container information for AZStd::array instance.");
}
const size_t size = container->Size(outputValue);
if (inputValue.Size() < size)
{
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Not enough entries in JSON array to load an AZStd::array from.");
}
ContinuationFlags flags = ContinuationFlags::None;
ContinuationFlags flags = isNewInstance ? ContinuationFlags::LoadAsNewInstance : ContinuationFlags::None;
Uuid elementTypeId = Uuid::CreateNull();
auto typeEnumCallback = [&elementTypeId, &flags](const Uuid&, const SerializeContext::ClassElement* genericClassElement)
{
@@ -175,13 +188,23 @@ namespace AZ
};
container->EnumTypes(typeEnumCallback);
const size_t size = container->Size(outputValue);
if (!isNewInstance && inputValue.Size() < size)
{
return context.Report(
JSR::Tasks::ReadField, JSR::Outcomes::Unsupported, "Not enough entries in JSON array to load an AZStd::array from.");
}
rapidjson::Value explicitDefaultValue = GetExplicitDefault();
JSR::ResultCode retVal(JSR::Tasks::ReadField);
for (size_t i = 0; i < size; ++i)
{
ScopedContextPath subPath(context, i);
void* element = container->GetElementByIndex(outputValue, nullptr, i);
JSR::ResultCode result = ContinueLoading(element, elementTypeId, inputValue[aznumeric_caster(i)], context, flags);
JSR::ResultCode result = ContinueLoading(
element, elementTypeId, isNewInstance ? explicitDefaultValue : inputValue[aznumeric_caster(i)], context, flags);
if (result.GetProcessing() == JSR::Processing::Halted)
{
return context.Report(result, "Failed to load data to element in AZStd::array.");
@@ -189,15 +212,19 @@ namespace AZ
retVal.Combine(result);
}
if (container->Size(outputValue) == inputValue.Size())
if (isNewInstance)
{
return context.Report(retVal, "Filled new instance of AZStd::array with defaults.");
}
else if (container->Size(outputValue) == inputValue.Size())
{
return context.Report(retVal, "Successfully read entries into AZStd::array.");
}
else
{
retVal.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Skipped));
return context.Report(retVal,
"Successfully read available entries into AZStd::array, but there were still values left in the JSON array.");
return context.Report(
retVal, "Successfully read available entries into AZStd::array, but there were still values left in the JSON array.");
}
}
} // namespace AZ
@@ -30,8 +30,14 @@ namespace AZ
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
protected:
JsonSerializationResult::Result LoadContainer(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonSerializationResult::Result LoadContainer(
void* outputValue,
const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue,
bool isNewInstance,
JsonDeserializerContext& context);
};
} // namespace AZ
@@ -34,11 +34,16 @@ namespace AZ
case rapidjson::kArrayType:
return LoadContainer(outputValue, outputValueTypeId, inputValue, context);
case rapidjson::kObjectType: // fall through
case rapidjson::kNullType: // fall through
case rapidjson::kStringType: // fall through
case rapidjson::kFalseType: // fall through
case rapidjson::kTrueType: // fall through
case rapidjson::kObjectType:
[[fallthrough]];
case rapidjson::kNullType:
[[fallthrough]];
case rapidjson::kStringType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
[[fallthrough]];
case rapidjson::kNumberType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Basic containers can only be read from an array.");
@@ -169,6 +174,7 @@ namespace AZ
ContinuationFlags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
? ContinuationFlags::ResolvePointer
: ContinuationFlags::None;
flags |= ContinuationFlags::LoadAsNewInstance;
const size_t capacity = container->IsFixedCapacity() ? container->Capacity(outputValue) : std::numeric_limits<size_t>::max();
@@ -247,6 +253,11 @@ namespace AZ
}
size_t addedCount = container->Size(outputValue) - containerSize;
if (addedCount > 0)
{
// Values were added which means the container is no longer in its default state of being emtpy.
retVal.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Success));
}
AZStd::string_view message =
addedCount >= arraySize ? "Successfully read basic container.":
addedCount == 0 ? "Unable to read data for basic container." :
@@ -190,6 +190,12 @@ namespace AZ
}
size_t addedCount = container->Size(outputValue) - containerSize;
if (addedCount > 0)
{
// If at least one entry was added then the map is no longer in it's default state so
// mark is with success so the result can at best be partial defaults.
retVal.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Success));
}
AZStd::string_view message =
addedCount >= maximumSize ? "Successfully read associative container." :
addedCount == 0 ? "Unable to read data for the associative container." :
@@ -215,10 +221,10 @@ namespace AZ
// Load key
void* keyAddress = pairContainer->GetElementByIndex(address, pairElement, 0);
AZ_Assert(keyAddress, "Element reserved for associative container, but unable to retrieve address of the key.");
ContinuationFlags keyLoadFlags = ContinuationFlags::None;
ContinuationFlags keyLoadFlags = ContinuationFlags::LoadAsNewInstance;
if (keyElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
{
keyLoadFlags = ContinuationFlags::ResolvePointer;
keyLoadFlags |= ContinuationFlags::ResolvePointer;
*reinterpret_cast<void**>(keyAddress) = nullptr;
}
JSR::ResultCode keyResult = ContinueLoading(keyAddress, keyElement->m_typeId, key, context, keyLoadFlags);
@@ -231,10 +237,10 @@ namespace AZ
// Load value
void* valueAddress = pairContainer->GetElementByIndex(address, pairElement, 1);
AZ_Assert(valueAddress, "Element reserved for associative container, but unable to retrieve address of the value.");
ContinuationFlags valueLoadFlags = ContinuationFlags::None;
ContinuationFlags valueLoadFlags = ContinuationFlags::LoadAsNewInstance;
if (valueElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
{
valueLoadFlags = ContinuationFlags::ResolvePointer;
valueLoadFlags |= ContinuationFlags::ResolvePointer;
*reinterpret_cast<void**>(valueAddress) = nullptr;
}
JSR::ResultCode valueResult = ContinueLoading(valueAddress, valueElement->m_typeId, value, context, valueLoadFlags);
@@ -34,13 +34,24 @@ namespace AZ
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
return LoadContainer(outputValue, outputValueTypeId, inputValue, context);
return LoadContainer(outputValue, outputValueTypeId, inputValue, false, context);
case rapidjson::kObjectType: // fall through
case rapidjson::kNullType: // fall through
case rapidjson::kStringType: // fall through
case rapidjson::kFalseType: // fall through
case rapidjson::kTrueType: // fall through
case rapidjson::kObjectType:
if (IsExplicitDefault(inputValue))
{
// Because this serializer has only the operation flag "InitializeNewInstance" set, the only time this will be called with
// an explicit default is when a new instance has been created.
return LoadContainer(outputValue, outputValueTypeId, inputValue, true, context);
}
[[fallthrough]];
case rapidjson::kNullType:
[[fallthrough]];
case rapidjson::kStringType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
[[fallthrough]];
case rapidjson::kNumberType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. AZStd::pair or AZStd::tuple can only be read from an array.");
@@ -127,8 +138,13 @@ namespace AZ
}
}
auto JsonTupleSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
JsonSerializationResult::Result JsonTupleSerializer::LoadContainer(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
const rapidjson::Value& inputValue, bool isNewInstance, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used to remove name conflicts in AzCore in uber builds.
@@ -154,7 +170,7 @@ namespace AZ
};
container->EnumTypes(typeCountCallback);
rapidjson::SizeType arraySize = inputValue.Size();
rapidjson::SizeType arraySize = isNewInstance ? typeCount : inputValue.Size();
if (arraySize < typeCount)
{
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
@@ -171,46 +187,73 @@ namespace AZ
container->EnumTypes(typeEnumCallback);
JSR::ResultCode retVal(JSR::Tasks::ReadField);
rapidjson::SizeType arrayIndex = 0;
size_t numElementsWritten = 0;
for (size_t i = 0; i < typeCount; ++i)
if (isNewInstance)
{
ScopedContextPath subPath(context, i);
void* elementAddress = container->GetElementByIndex(outputValue, nullptr, i);
AZ_Assert(elementAddress, "Address of AZStd::pair or AZStd::tuple element %zu could not be retrieved.", i);
rapidjson::Value explicitDefaultValue = GetExplicitDefault();
ContinuationFlags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
? ContinuationFlags::ResolvePointer
: ContinuationFlags::None;
while (arrayIndex < inputValue.Size())
for (size_t i = 0; i < typeCount; ++i)
{
JSR::ResultCode result = ContinueLoading(elementAddress, classElements[i]->m_typeId, inputValue[arrayIndex], context, flags);
ScopedContextPath subPath(context, i);
void* elementAddress = container->GetElementByIndex(outputValue, nullptr, i);
AZ_Assert(elementAddress, "Address of AZStd::pair or AZStd::tuple element %zu could not be retrieved.", i);
ContinuationFlags flags = ContinuationFlags::LoadAsNewInstance;
flags |=
(classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ? ContinuationFlags::ResolvePointer
: ContinuationFlags::None);
JSR::ResultCode result = ContinueLoading(elementAddress, classElements[i]->m_typeId, explicitDefaultValue, context, flags);
retVal.Combine(result);
arrayIndex++;
if (result.GetProcessing() == JSR::Processing::Halted)
{
return context.Report(retVal, "Failed to read element for AZStd::pair or AZStd::tuple.");
}
else if (result.GetProcessing() != JSR::Processing::Altered)
{
numElementsWritten++;
break;
}
}
}
if (numElementsWritten < typeCount)
{
AZStd::string_view message = numElementsWritten == 0 ?
"Unable to read data for AZStd::pair or AZStd::tuple." :
"Partially read data for AZStd::pair or AZStd::tuple.";
return context.Report(retVal, message);
return context.Report(retVal, "Initialized AZStd::pair or AZStd::tuple to defaults.");
}
else
{
return context.Report(retVal, "Successfully read AZStd::pair or AZStd::tuple.");
rapidjson::SizeType arrayIndex = 0;
size_t numElementsWritten = 0;
for (size_t i = 0; i < typeCount; ++i)
{
ScopedContextPath subPath(context, i);
void* elementAddress = container->GetElementByIndex(outputValue, nullptr, i);
AZ_Assert(elementAddress, "Address of AZStd::pair or AZStd::tuple element %zu could not be retrieved.", i);
ContinuationFlags flags =
(classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ? ContinuationFlags::ResolvePointer
: ContinuationFlags::None);
while (arrayIndex < inputValue.Size())
{
JSR::ResultCode result =
ContinueLoading(elementAddress, classElements[i]->m_typeId, inputValue[arrayIndex], context, flags);
retVal.Combine(result);
arrayIndex++;
if (result.GetProcessing() == JSR::Processing::Halted)
{
return context.Report(retVal, "Failed to read element for AZStd::pair or AZStd::tuple.");
}
else if (result.GetProcessing() != JSR::Processing::Altered)
{
numElementsWritten++;
break;
}
}
}
if (numElementsWritten < typeCount)
{
AZStd::string_view message = numElementsWritten == 0 ? "Unable to read data for AZStd::pair or AZStd::tuple."
: "Partially read data for AZStd::pair or AZStd::tuple.";
return context.Report(retVal, message);
}
else
{
return context.Report(retVal, "Successfully read AZStd::pair or AZStd::tuple.");
}
}
}
} // namespace AZ
@@ -23,13 +23,20 @@ namespace AZ
public:
AZ_RTTI(JsonTupleSerializer, "{1AA0ADC1-395A-4223-8A73-304ACDEE7793}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
private:
JsonSerializationResult::Result LoadContainer(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonSerializationResult::Result LoadContainer(
void* outputValue,
const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue,
bool isNewInstance,
JsonDeserializerContext& context);
};
} // namespace AZ