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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user