{"id":21972032,"url":"https://github.com/icfnext/sling-object-map","last_synced_at":"2025-07-13T05:38:20.590Z","repository":{"id":146370908,"uuid":"70711369","full_name":"icfnext/sling-object-map","owner":"icfnext","description":null,"archived":false,"fork":false,"pushed_at":"2018-12-13T20:36:59.000Z","size":59,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":7,"default_branch":"develop","last_synced_at":"2025-01-28T02:42:08.032Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/icfnext.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-10-12T15:02:21.000Z","updated_at":"2019-01-23T21:11:26.000Z","dependencies_parsed_at":null,"dependency_job_id":"e9ee5d35-b4c6-44da-a7b6-06fd8e5ae5a3","html_url":"https://github.com/icfnext/sling-object-map","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icfnext%2Fsling-object-map","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icfnext%2Fsling-object-map/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icfnext%2Fsling-object-map/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icfnext%2Fsling-object-map/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/icfnext","download_url":"https://codeload.github.com/icfnext/sling-object-map/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245031517,"owners_count":20549925,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-29T15:00:55.992Z","updated_at":"2025-03-22T23:12:21.696Z","avatar_url":"https://github.com/icfnext.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sling Object Map #\n\nSling Object Map is a library that provides fast, lossless read/write/merge of arbitraty Java objects to/from a Sling resource tree (i.e., to a JCR repository, or any other system for which a modifiable resource provider is configured). It works entirely via reflection -- no annotations on target classes are required.\n\n### Type Support ##\n\nAny POJO class can be written, provided it meets the following requirements:\n\n* All properties are accessible via Java-beans getters and setters\n* All properties are either basic types (see below) or an object type composed of basic types (fully-recursive)\n* Basic types include:\n  * String\n  * Boolean\n  * Integer\n  * Long\n  * Float\n  * Double\n  * Date\n  * Calendar\n  * Enum values\n  * Java collection types (Collections, Lists, Sets, Maps)\n  * Arrays (of both basic and non-basic values)\n\nClients can add support for additional types by providing functions to serialize/deserialize their types to a Sling resource.\n\n## Features ##\n\nAny previously-written POJO can subsequently be read without loss, provided it has an accessible default constructor.  The OOTB system can be extended to handle classes that don't meet these requirements.  The library also provides limited support for cyclic object trees, provided the self-referencing types implement equals().\n\nIn addition to basic read/write operations, the library can merge a java object to a previously-written resource.  Only updated properties will be written to the resource tree, and a list of the changed properties/sub-resources will be returned.\n\n## Serialization Format ##\n\nObjects are written to the Sling resource tree in a format that tracks the runtime class (and the bundle that loaded the class) to ensure lossless deserialization.  For example, an instance of the following class...\n\n```java\npackage example;\n\npublic class Example {\n\n\tprivate String value = \"test\";\n\t\n\tpublic String getValue() {\n\t\treturn value;\n\t}\n\t\n\tpublic void setValue(final String value) {\n\t\tthis.value = value;\n\t}\n}\n```\n\n...would be written as the following resource tree (when persisted to a JCR repository):\n\n```\n+ resource-name (nt:unstructured)\n  - map:runtimeClass (string) = \"example.Example\"\n  - map:bundleName (string) = \"example-bundle\"\n  - value (string) = \"test\"\n```\n\nObject trees that can't be written as a single resource will be written as a tree of resources:\n\n```java\npackage example;\n\npublic class ExampleContainer {\n\n\tprivate Example contained = new Example();\n\t\n\tpublic Example getContained() {\n\t\treturn contained;\n\t}\n\t\n\tpublic void setContained(final Example contained) {\n\t\tthis.contained = contained;\n\t}\n}\n```\n\nWould be written as:\n\n```\n+ resource-name (nt:unstructured)\n  - map:runtimeClass (string) = \"example.ExampleContainer\"\n  - map:bundleName (string) = \"example-bundle\"\n  + contained (nt:unstructured)\n    - map:runtimeClass (string) = \"example.Example\"\n    - map:bundleName (string) = \"example-bundle\"\n    - value (string) = \"test\"\n```\n\n### Usage ##\n\nThe example below shows the basic flow for building an ObjectMapper instance and writing/reading/merging an object tree.\n\n```java\npackage com.icfolson.sling.slingmap.runtime;\n\nimport com.icfolson.sling.slingmap.api.basictype.BasicTypeRegistry;\nimport com.icfolson.sling.slingmap.api.domain.MergeResult;\nimport com.icfolson.sling.slingmap.api.exception.MappingException;\nimport com.icfolson.sling.slingmap.api.generator.MappingGenerator;\nimport com.icfolson.sling.slingmap.api.mapper.ObjectMapper;\nimport com.icfolson.sling.slingmap.api.registry.ReaderWriterRegistry;\nimport com.icfolson.sling.slingmap.runtime.basictype.JcrBasicTypeRegistry;\nimport com.icfolson.sling.slingmap.runtime.generator.JcrMappingGeneratorDecorator;\nimport com.icfolson.sling.slingmap.runtime.generator.ReflectionMappingGenerator;\nimport com.icfolson.sling.slingmap.runtime.mapper.DefaultObjectMapper;\nimport com.icfolson.sling.slingmap.runtime.registry.DefaultReaderWriterRegistry;\nimport org.apache.sling.api.resource.PersistenceException;\nimport org.apache.sling.api.resource.Resource;\nimport org.apache.sling.api.resource.ResourceResolver;\n\npublic class Usage {\n\n    public void exampleUsage(ResourceResolver resolver) throws MappingException, PersistenceException {\n        BasicTypeRegistry basicTypeRegistry = new JcrBasicTypeRegistry();\n        // Add new basic types, if desired\n        MappingGenerator generator = new ReflectionMappingGenerator(basicTypeRegistry); // Automatically generate mappings via reflection\n        generator = new JcrMappingGeneratorDecorator(generator); // Add JCR-specific handling (node types)\n        ReaderWriterRegistry registry = new DefaultReaderWriterRegistry(generator);\n        // Register custom reader/writer/merger implementations, if desired\n        ObjectMapper mapper = new DefaultObjectMapper(registry);\n\n        Example a = new Example(\"A\");\n        Example b = new Example(\"B\");\n        Example c = new Example(\"C\");\n        \n        a.setNext(b);\n        b.setNext(c);\n        c.setNext(a);  // Object graphs don't need to be acyclic\n\n        Resource target = resolver.resolve(\"/test\"); // May be non-existing\n        mapper.writeObject(a, target);\n        resolver.commit(); // Changes are not saved automatically\n\n        /*\n\n        The resulting structure is written at /test\n\n        + test\n          - jcr:primaryType = \"nt:unstructured\"\n          - map:runtimeClass = \"com.icfolson.sling.slingmap.runtime.Usage$Example\"\n          - map:bundleName = \"sling-object-map-runtime\"\n          - name = \"A\"\n          + next\n            - jcr:primaryType = \"nt:unstructured\"\n            - map:runtimeClass = \"com.icfolson.sling.slingmap.runtime.Usage$Example\"\n            - map:bundleName = \"sling-object-map-runtime\"\n            - name = \"B\"\n            + next\n              - jcr:primaryType = \"nt:unstructured\"\n              - map:runtimeClass = \"com.icfolson.sling.slingmap.runtime.Usage$Example\"\n              - map:bundleName = \"sling-object-map-runtime\"\n              - name = \"C\"\n              + next\n                - jcr:primaryType = \"nt:unstructured\"\n                - map:referencePath = \"/test\"\n         */\n\n        target = resolver.resolve(\"/test\");\n        Example readValue = mapper.readObject(target, Example.class);\n        assert readValue.equals(a);\n        assert readValue.getNext().equals(b);\n        assert readValue.getNext().getNext().equals(c);\n        assert readValue.getNext().getNext().getNext().equals(a);\n\n        a.setName(null);\n        b.setName(\"b\");\n        c.setNext(null);\n        target = resolver.resolve(\"/test\");\n        MergeResult result = mapper.mergeObject(a, target);\n        resolver.commit();\n\n        /*\n\n        Result contains three changes:\n\n        - /@name: DELETE\n        - /next/@name: UPDATE\n        - /next/next/next: DELETE\n\n        The resulting structure is written at /test\n\n        + test\n          - jcr:primaryType = \"nt:unstructured\"\n          - map:runtimeClass = \"com.icfolson.sling.slingmap.runtime.Usage$Example\"\n          - map:bundleName = \"sling-object-map-runtime\"\n          + next\n            - jcr:primaryType = \"nt:unstructured\"\n            - map:runtimeClass = \"com.icfolson.sling.slingmap.runtime.Usage$Example\"\n            - map:bundleName = \"sling-object-map-runtime\"\n            - name = \"b\"\n            + next\n              - jcr:primaryType = \"nt:unstructured\"\n              - map:runtimeClass = \"com.icfolson.sling.slingmap.runtime.Usage$Example\"\n              - map:bundleName = \"sling-object-map-runtime\"\n              - name = \"C\"\n         */\n\n    }\n\n    public static final class Example {\n\n        private String name;\n        private Example next;\n\n        public Example() { }\n\n        public Example(final String name) {\n            this.name = name;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        public void setName(final String name) {\n            this.name = name;\n        }\n\n        public Example getNext() {\n            return next;\n        }\n\n        public void setNext(final Example next) {\n            this.next = next;\n        }\n\n        @Override\n        public boolean equals(final Object o) {\n            if (this == o)\n                return true;\n            if (o == null || getClass() != o.getClass())\n                return false;\n\n            final Example example = (Example) o;\n\n            return name != null ? name.equals(example.name) : example.name == null;\n\n        }\n\n        @Override\n        public int hashCode() {\n            return name != null ? name.hashCode() : 0;\n        }\n    }\n\n}\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ficfnext%2Fsling-object-map","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ficfnext%2Fsling-object-map","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ficfnext%2Fsling-object-map/lists"}