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

Concept of map_all with JSON, YAML and TOML? #139

Closed
ronaldtse opened this issue Nov 1, 2024 · 3 comments · Fixed by #248
Closed

Concept of map_all with JSON, YAML and TOML? #139

ronaldtse opened this issue Nov 1, 2024 · 3 comments · Fixed by #248
Labels
question Further information is requested

Comments

@ronaldtse
Copy link
Contributor

I think what I don't understand is, how does the concept of map_all work with JSON, YAML and TOML?

JSON is like: { "attr" : "value" }. Does that mean map_all will give the raw value of { "attr" : "value" }?

YAML is like: attr: value. Does that mean map_all will give the raw value?

Does map_all on this element produce:

<element>
  <one>text</one>
</element>
  1. The entire element?
<element attr=1>
  <one>text</one>
</element>
  1. Only the inner content of the element?
  <one>text</one>

Originally posted by @ronaldtse in #129 (comment)

@ronaldtse ronaldtse added the question Further information is requested label Nov 1, 2024
@HassanAkbar
Copy link
Member

HassanAkbar commented Nov 1, 2024

@ronaldtse What i have in mind is that the map_all will work by assigning everything inside the tag or the key it is defined in. for example

  • For XML

    map_all will assign all the content inside that tag but will not assign the attributes in the current tag. i.e

    class Element < Lutaml::Model::Serializable
      attribute :all_content, :string
      
      xml do
        map_all to: :all_content
      end
    end
    <element attr1="1">
      <one attr2="2" attr3="3">text</one>
    </element>
    Element.from_xml(xml).all_content
    > "<one attr2=\"2\" attr3=\"3\">text</one>"
  • For YAML, JSON, and TOML

    map_all will assign all the content against the key to the model attribute. i.e

    class Element < Lutaml::Model::Serializable
      attribute :all_content, :string
      
      yaml do
        map_all to: :all_content
      end
      
      json do
        map_all to: :all_content
      end
    
      toml do
        map_all to: :all_content
      end
    end

    YAML Example

    element:
    - another_element
    - element_with_childs
      name: "John"
    Element.from_yaml(yaml).all_content
    > "- another_element\n- element_with_childs\n  name: \"John\""

    JSON Example

    { 
      "element": [
        "another_element",
        "element_with_childs": { "name": "John" }
      ]
    }
    Element.from_json(json).all_content
    > "[\"another_element\", \"element_with_childs\": {\"name\": \"John\"}]"

Am I going in the right direction or do we need to change something here?

@ronaldtse
Copy link
Contributor Author

I believe this is correct. I almost wanted to say that "an element that uses map_all means it is a leaf node and can be represented using a Value class", but it is not true because an XML element contains element content as well as attribute data.

We have seen cases where the XML element's attribute data are parsed but the element content is treated as map_all.

Since a Value class only has a single value, it cannot handle element content + attributes. So there is still value in map_all and map_attributes.

@HassanAkbar
Copy link
Member

@ronaldtse I have worked on it and realized that the above-given examples will change slightly because:
In XML, the implementation of map_all is around the parent node, and inner XML is fetched, while in key-value formats, We don’t have any parent node/element to fetch children for.

Even if we have a single key in the input hash, we still do not know about the key since we don’t have root in key-value formats just like we have in XML

So the implementation will go like this: “fetch the whole input and then serialize it to store it in the attribute, which is mapped using map_all”
For example:

Sample Input:

        {
          "sections" => [
            { "title" => "Introduction", "text" => "Chapter 1" },
            { "title" => "Conclusion", "text" => "Final chapter" },
          ],
          "metadata" => {
            "author" => "John Doe",
            "date" => "2024-01-15",
          },
        }.to_json

In the above sample input, we don’t have any parent node whose children we can fetch and assign the attribute value after serializing it.
So what we can do is we will fetch the whole input above, serialize it into the respective format, and then we assign the value of map_all attribute to the serialized string.

It will become:

class Document < Lutaml::Model::Serializable
    attribute :content, :string

    json do
      map_all to: :content
    end
  end

doc = Document.from_json(json_content)
puts doc.content

#=> "{\"sections\":[{\"title\":\"Introduction\",\"text\":\"Chapter 1\"},{\"title\":\"Conclusion\",\"text\":\"Final chapter\"}],\"metadata\":{\"author\":\"John Doe\",\"date\":\"2024-01-15\"}}"

Let me know what do you suggest about this. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants