Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 69 additions & 2 deletions lib/avro/schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ static function real_parse($avro, $default_namespace=null, &$schemata=null)
$type));
}
elseif (self::is_primitive_type($avro))
return new AvroPrimitiveSchema($avro, null, [], true);
return new AvroPrimitiveSchema($avro, null, [], false);
else
throw new AvroSchemaParseException(
sprintf('%s is not a schema we know about.',
Expand Down Expand Up @@ -1600,7 +1600,7 @@ public function to_avro()
? $this->type->qualified_name() : $this->type->to_avro();

if ($this->has_default)
$avro[AvroField::DEFAULT_ATTR] = $this->default;
$avro[AvroField::DEFAULT_ATTR] = $this->serialize_default_value($this->default, $this->type);

if ($this->order)
$avro[AvroField::ORDER_ATTR] = $this->order;
Expand All @@ -1620,6 +1620,73 @@ public function to_avro()
return $avro;
}

/**
* Converts default value so that map/record serialize as JSON {} not [].
*
* Problem: PHP json_encode([]) produces "[]", but Avro expects "{}" for empty map/record.
* Solution: Convert PHP arrays to stdClass for map/record types.
*
* @param mixed $value
* @param AvroSchema $schema
* @return mixed
*/
private function serialize_default_value($value, AvroSchema $schema)
{
if (!is_array($value))
return $value;

$type = $schema->type();

// map/record: convert to stdClass (JSON object)
if ($type === AvroSchema::MAP_SCHEMA)
return $this->array_to_object($value, $schema->values());

if ($type === AvroSchema::RECORD_SCHEMA || $type === AvroSchema::ERROR_SCHEMA)
return $this->record_to_object($value, $schema);

// array: keep as array, but process items recursively
if ($type === AvroSchema::ARRAY_SCHEMA)
return array_map(fn($item) => $this->serialize_default_value($item, $schema->items()), $value);

// union: check if empty array should be object
if ($schema instanceof AvroUnionSchema)
return $this->serialize_union_default($value, $schema);

return $value;
}

private function array_to_object(array $value, AvroSchema $items_schema): \stdClass
{
$obj = new \stdClass();
foreach ($value as $k => $v)
$obj->$k = $this->serialize_default_value($v, $items_schema);
return $obj;
}

private function record_to_object(array $value, AvroSchema $schema): \stdClass
{
$obj = new \stdClass();
$fields = $schema->fields_hash();
foreach ($value as $k => $v)
$obj->$k = isset($fields[$k])
? $this->serialize_default_value($v, $fields[$k]->type)
: $v;
return $obj;
}

private function serialize_union_default(array $value, AvroUnionSchema $schema)
{
if (!empty($value))
return $value;

// empty array + union contains map/record = convert to object
foreach ($schema->schemas() as $branch)
if ($branch->type() === AvroSchema::MAP_SCHEMA || $branch->type() === AvroSchema::RECORD_SCHEMA)
return (object) $value;

return $value;
}

/**
* @return string the name of this field
*/
Expand Down
Loading