Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added root_mappings option #222

Merged
merged 6 commits into from
Jan 9, 2025
Merged

feat: added root_mappings option #222

merged 6 commits into from
Jan 9, 2025

Conversation

HassanAkbar
Copy link
Member

@HassanAkbar HassanAkbar commented Jan 1, 2025

Added support for root mappings. It allows the key-value pair with a dynamic name. The values are extracted from the key-value data model using the root key value.

Other mappings like map_element can't be defined with root_mappings.

closes #216

@ronaldtse
Copy link
Contributor

Instead of calling it root_mappings maybe it should just be root_map or better as map_root?

Because the key_value method is just map.

@ronaldtse
Copy link
Contributor

@HassanAkbar the feature is not fully functional yet. I've provided new examples in the README and they are supposed to work (I believe).

For example the simplest example:

yaml = "---
vase1:
  name: Imperial Vase
bowl2:
  name: 18th Century Bowl
"

# This is a normal Lutaml::Model class
class Ceramic < Lutaml::Model::Serializable
  attribute :ceramic_id, :string
  attribute :ceramic_name, :string

  key_value do
    map 'id', to: :ceramic_id
    map 'name', to: :ceramic_name
  end
end

# This is Lutaml::Model class that represents the collection of Ceramic objects
class CeramicCollection < Lutaml::Model::Serializable
  attribute :ceramics, Ceramic, collection: true

  key_value do
    map to: :ceramics, # All data goes to the `ceramics` attribute
      root_mappings: {
        # The key of an object in this collection is mapped to the ceramic_id
        # attribute of the Ceramic object.
        ceramic_id: :key # "key" is a reserved keyword
      }
  end
end

# Parsing the YAML collection with dynamic data keys
ceramic_collection = CeramicCollection.from_yaml(yaml)

## It's not reading the values at all
=> 
#<CeramicCollection:0x000000013a7b2e98
 @ceramics=
  [#<Ceramic:0x000000013a7b21c8 @ceramic_id=nil, @ceramic_name=nil>,
   #<Ceramic:0x000000013a7b1a70 @ceramic_id=nil, @ceramic_name=nil>]>

first_ceramic = ceramic_collection.ceramics.first
=> #<Ceramic:0x000000013a7b21c8 @ceramic_id=nil, @ceramic_name=nil>

# NOTE: When the collection is serialized, the `ceramic_id` attribute is used to
# key the data. This is defined through the `map` with `root_mappings` method in
# CeramicCollection.
> new_collection = CeramicCollection.new(ceramics: [
    Ceramic.new(ceramic_id: "vase1", ceramic_name: "Imperial Vase"),
    Ceramic.new(ceramic_id: "bowl2", ceramic_name: "18th Century Bowl")
  ])

# When used individually, the object is correct
> new_collection.ceramics.first
=> #<Ceramic:0x000000013c030ee8 @ceramic_id="vase1", @ceramic_name="Imperial Vase">
> new_collection.ceramics.first.to_yaml
=> "---\nid: vase1\nname: Imperial Vase\n"


# Serializing the collection is broken
> new_collection.to_yaml
=> "---\nvase1: {}\nbowl2: {}\n"

# The correct output is this:
# ---
# vase1:
#   name: Imperial Vase
# bowl2:
#   name: 18th Century Bowl

@ronaldtse
Copy link
Contributor

@HassanAkbar can you please help go through the 3 examples I provided in the README and add them as Specs? Thanks.

Copy link
Contributor

@ronaldtse ronaldtse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need fixing...

@HassanAkbar
Copy link
Member Author

HassanAkbar commented Jan 7, 2025

@ronaldtse All the examples that you have added are passing except the last one which is

# This is Lutaml::Model class that represents the collection of Ceramic objects
class CeramicCollection < Lutaml::Model::Serializable
  attribute :ceramics, Ceramic, collection: true

  key_value do
    map to: :ceramics, # All data goes to the `ceramics` attribute
      root_mappings: {
        # The key of an object in this collection is mapped to the ceramic_id
        # attribute of the Ceramic object.
        key: :ceramic_id # "key" is a reserved keyword
      }
  end
end

> new_collection = CeramicCollection.new(ceramics: [
    Ceramic.new(ceramic_id: "vase1", ceramic_name: "Imperial Vase"),
    Ceramic.new(ceramic_id: "bowl2", ceramic_name: "18th Century Bowl")
  ])
> puts new_collection.to_yaml

Expected Output:

# ---
# vase1:
#   name: Imperial Vase
# bowl2:
#   name: 18th Century Bowl

Actual Output:

# vase1:
# bowl2:

This is happening because we are using the root_mappings to generate the output and there is no mapping defined for the name only the key is defined. So only the key is present in the output.

I think this is the correct behavior because we don't know how to handle name without the mappings. Should we merge this or do we need to change this?

@HassanAkbar HassanAkbar requested a review from ronaldtse January 8, 2025 08:08
@ronaldtse
Copy link
Contributor

@HassanAkbar sorry it seems that my changes to the README somehow disappeared. I have pushed some README changes that adopted the approach from our discussion for attribute mapping, i.e.

  • :{attribute_name} => :key or
  • :{attribute_name} => [ ... path... ]

Please have a look and let me know if it works. Thanks!

@ronaldtse ronaldtse merged commit 35df2c3 into main Jan 9, 2025
2 checks passed
@ronaldtse
Copy link
Contributor

Thank you @HassanAkbar ! This is now merged.

@ronaldtse ronaldtse deleted the collection-mappings branch January 9, 2025 06:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

child_mappings for root
2 participants