{"id":20841505,"url":"https://github.com/wavesoftware/java-stringify-object","last_synced_at":"2026-04-20T18:32:33.134Z","repository":{"id":32037748,"uuid":"130129262","full_name":"wavesoftware/java-stringify-object","owner":"wavesoftware","description":"A utility to safely inspect any Java Object as string representation","archived":false,"fork":false,"pushed_at":"2022-02-09T23:12:22.000Z","size":204,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"develop","last_synced_at":"2025-07-26T05:39:28.660Z","etag":null,"topics":["hibernate","jpa","stringify","tostring"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wavesoftware.png","metadata":{"files":{"readme":"README.adoc","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}},"created_at":"2018-04-18T22:32:59.000Z","updated_at":"2019-08-10T10:33:28.000Z","dependencies_parsed_at":"2022-08-07T17:01:31.997Z","dependency_job_id":null,"html_url":"https://github.com/wavesoftware/java-stringify-object","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/wavesoftware/java-stringify-object","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wavesoftware%2Fjava-stringify-object","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wavesoftware%2Fjava-stringify-object/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wavesoftware%2Fjava-stringify-object/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wavesoftware%2Fjava-stringify-object/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wavesoftware","download_url":"https://codeload.github.com/wavesoftware/java-stringify-object/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wavesoftware%2Fjava-stringify-object/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32059766,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T11:35:06.609Z","status":"ssl_error","status_checked_at":"2026-04-20T11:34:48.899Z","response_time":94,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["hibernate","jpa","stringify","tostring"],"created_at":"2024-11-18T01:20:25.068Z","updated_at":"2026-04-20T18:32:33.117Z","avatar_url":"https://github.com/wavesoftware.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"= Stringify Object for Java\n\nhttps://travis-ci.org/wavesoftware/java-stringify-object[image:https://travis-ci.org/wavesoftware/java-stringify-object.svg?branch=master[Build\nStatus]]\nhttps://sonarcloud.io/dashboard?id=wavesoftware_java-stringify-object[image:https://sonarcloud.io/api/project_badges/measure?project=wavesoftware_java-stringify-object\u0026metric=alert_status[Quality Gate]]\nhttps://sonarcloud.io/dashboard?id=wavesoftware_java-stringify-object[image:https://sonarcloud.io/api/project_badges/measure?project=wavesoftware_java-stringify-object\u0026metric=coverage[Coverage]]\nhttps://bintray.com/bintray/jcenter/pl.wavesoftware.utils%3Astringify-object[image:https://img.shields.io/maven-central/v/pl.wavesoftware.utils/stringify-object.svg[Maven\nCentral]]\n\nA utility to safely inspect any Java Object as String representation.\nIt's best to be used with domain model (also with JPA entities) with\nintention to dump those entities as text to log files.\n\nIt runs in two modes: `+PROMISCUOUS+` (by default) and `+QUIET+`. In\n`+PROMISCUOUS+` mode every defined field is automatically inspected,\nunless the field is annotated with `+@DoNotInspect+` annotation. In\n`+QUIET+` mode only fields annotated with `+@Inspect+` will gets\ninspected.\n\nThere is also support for masking out sensitive data that may occur in objects. To do that, annotate field with `+@Mask+` annotation specifying a `+Masker+` implementation you prepared.\n\nThis library has proper support for object graph cycles, and JPA\n(Hibernate) lazy loaded elements. There is support for output themes.\n\n== Usage\n\n=== In Promiscuous mode\n\n[source,java]\n----\n// In PROMISCUOUS mode define fields to exclude\nclass Person {\n  private int id;\n  @DisplayNull // \u003c1\u003e\n  private Person parent;\n  private List\u003cPerson\u003e childs;\n  private Account account;\n  @Inspect(conditionally = IsInDevelopment.class) // \u003c2\u003e\n  private String password;\n  @DoNotInspect\n  private String ignored;\n  @Mask(SocialIdNumberMasker.class) // \u003c3\u003e\n  private String socialNumber;\n}\n  \n// inspect an object  \nPerson person = query.getSingleResult();\nStringify stringify = Stringify.of(person);\nstringify.mode(Mode.PROMISCUOUS);\n// stringify.beanFactory(...);\nassert \"\u003cPerson id=15, parent=\u003cPerson id=16, parent=null, \"\n + \"childs=[(↻)], account=⁂Lazy, socialNumber=\\\"455*********\\\"\u003e, childs=[], \"\n + \"account=⁂Lazy, socialNumber=\\\"156*********\\\"\u003e\".equals(stringify.toString());\n----\n\n\u003c1\u003e Configures a field inspection to show null values\n\u003c2\u003e Inspects a field, but only if given predicate returns `true`\n\u003c3\u003e Mask outs a value with user provided `+Masker+` implementation.\n\n=== In Quiet mode\n\n[source,java]\n----\nimport pl.wavesoftware.utils.stringify.api.Inspect;// In QUIET mode define fields to inspect\nclass Person {  \n  @Inspect private int id;\n  @Inspect @DisplayNull private Person parent;\n  @Inspect private List\u003cPerson\u003e childs;\n  @Inspect private Account account;\n  private String ignored;\n  @Inspect @Mask(SocialIdNumberMasker.class)\n  private String socialNumber;\n}\n  \n// inspect an object  \nPerson person = query.getSingleResult();\nStringify stringify = Stringify.of(person);\nstringify.mode(Mode.QUIET);\nassert \"\u003cPerson id=15, parent=\u003cPerson id=16, parent=null, \"\n + \"childs=[(↻)], account=⁂Lazy, socialNumber=\\\"455*********\\\"\u003e, childs=[], \"\n + \"account=⁂Lazy, socialNumber=\\\"156*********\\\"\u003e\".equals(stringify.toString());\n----\n\n== Features\n\n* String representation of any Java class in two modes `+PROMISCUOUS+`\nand `+QUIET+`.\n* Fine tuning of which fields to display\n* Supports a masking of sensitive data, with `+@Mask+` annotation.\n* Support for cycles in object graph - `+(↻)+` is displayed instead, by default.\n* Support for Hibernate lazy loaded entities - `+⁂Lazy+` is displayed\ninstead, by default.\n* Full support for themes with indentation control. See a https://github.com/wavesoftware/java-stringify-object/blob/develop/src/test/java/pl/wavesoftware/utils/stringify/spi/theme/PrettyPrintTheme.java[PrettyPrintTheme] in test code, as an example.\n\n[[vs-lombok-tostring]]\n== vs. Lombok @ToString\n\nStringify Object for Java is designed for *slightly different* use case\nthen Lombok.\n\nLombok `+@ToString+` is designed to quickly inspect fields of simple\nobjects by generating static simple implementation of this mechanism.\n\nStringify Object for Java is designed to inspect complex objects that\ncan have cycles and can be managed by JPA provider like Hibernate\n(introducing Lazy Loading problems).\n\n=== Pros of Lombok vs Stringify Object\n\n* Lombok is *fast* - it's statically generated code without using\nReflection API.\n* Lombok is *easy* - it's zero configuration in most cases.\n\n=== Cons of Lombok vs Stringify Object\n\n* Lombok can't *detect cycles* is object graph, which implies\n`+StackOverflowException+` being thrown in that case\n* Lombok can't detect a *lazy loaded entities*, which leads to force\nloading it from JPA by invoking SQL statements. It's typical *n+1\nproblem*, but with nasty consequences - your `+toString()+` method is\ninvoking SQL without your knowledge!!\n\n== Configuration\n\nConfiguration is done in two ways: declarative - using Java's service\nloader mechanism, and programmatic.\n\n=== Configuration using Service Loader\n\nA `+Configurator+` interface is intended to be implemented in user code,\nand assigned to https://www.baeldung.com/java-spi[Service Loader]\nmechanism.\n\nTo do that, create on your classpath, a file:\n\n`+/META-INF/services/pl.wavesoftware.utils.stringify.spi.Configurator+`\n\nIn that file, place a fully qualified class name of your class that\nimplements `+Configurator+` interface. It should be called first time\nyou use an Stringify to inspect an object:\n\n....\n# classpath:/META-INF/services/pl.wavesoftware.utils.stringify.spi.Configurator\norg.acmecorp.StringifyConfigurator\n....\n\nThen implement that class in your code:\n\n[source,java]\n----\npackage org.acmecorp;\n\nimport pl.wavesoftware.utils.stringify.api.Configuration;\nimport pl.wavesoftware.utils.stringify.spi.Configurator;\n\npublic final class StringifyConfigurator implements Configurator {\n  \n  @Override\n  public void configure(Configuration configuration) {\n    configuration.beanFactory(new SpringBeanFactory());\n  }\n}\n----\n\nwith example Spring based BeanFactory:\n\n[source,java]\n----\npackage org.acmecorp;\n\nimport org.springframework.context.event.ContextRefreshedEvent;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Configuration;\n\nimport pl.wavesoftware.utils.stringify.spi.BeanFactory;\nimport pl.wavesoftware.utils.stringify.spi.BootingAware;\n\n@Configuration\nclass SpringBeanFactory implements BeanFactory, BootingAware {\n  private static ApplicationContext context;\n  \n  @EventListener(ContextRefreshedEvent.class)\n  void onRefresh(ContextRefreshedEvent event) {\n    SpringBeanFactory.context = event.getApplicationContext();\n  }\n  \n  @Override\n  public \u003cT\u003e T create(Class\u003cT\u003e contractClass) {\n    return SpringBeanFactory.context.getBean(contractClass);\n  }\n\n  @Override\n  public boolean isReady() {\n    return SpringBeanFactory.context != null;\n  }\n}\n----\n\n=== Programmatic configuration\n\nYou can also fine tune you configuration on instance level - using\nmethods available at `+Stringify+` interface:\n\n[source,java]\n----\n// given\nBeanFactory beanFactory = createBeanFactory();\nPerson person = createPerson();\n\n// then\nStringify stringifier = Stringify.of(person);\nstringifier\n  .beanFactory(beanFactory)\n  .mode(Mode.QUIET)\n  .stringify();\n----\n\n== Themes\n\nBe default a theme is set which is safe to use in JPA environment and it should be safe to use in logging, as output that it produces is rendered in single line.\n\nIf you prefer some tweaks to the theme, you can easily do that by implementing some methods from `+Theme+` interface:\n\n[source,java]\n----\nfinal class RecursionIdTheme implements Theme {\n  private static CharSequence name(Object target) {\n    return target.getClass().getSimpleName()\n      + \"@\"\n      + Integer.toUnsignedString(System.identityHashCode(target), 36);\n  }\n\n  @Override\n  public ComplexObjectStyle complexObject() {\n    return new ComplexObjectStyle() {\n\n      @Override\n      public CharSequence name(InspectionPoint point) {\n        return RecursionIdTheme.name(point.getValue().get());\n      }\n    };\n  }\n\n  @Override\n  public RecursionStyle recursion() {\n    return new RecursionStyle() {\n      @Override\n      public CharSequence representation(InspectionPoint point) {\n        return \"(↻️️ \" + RecursionIdTheme.name(point.getValue().get()) + \")\";\n      }\n    };\n  }\n}\n----\n\nTo set use `+Configurator+`, described above in detail, or set theme to instance of `+Stringify+` object:\n\n[source,java]\n----\nStringify.of(target)\n  .theme(new RecursionIdTheme())\n  .stringify();\n----\n\nNOTE: Look up a class https://github.com/wavesoftware/java-stringify-object/blob/develop/src/test/java/pl/wavesoftware/utils/stringify/spi/theme/PrettyPrintTheme.java[PrettyPrintTheme] in test code for a more complete example of theme usage. That's with indentation control and options.\n\n== Dependencies\n\n* Java \u003e= 8\n* https://github.com/wavesoftware/java-eid-exceptions[EID Exceptions]\nlibrary\n\n=== Contributing\n\nContributions are welcome!\n\nTo contribute, follow the standard\nhttp://danielkummer.github.io/git-flow-cheatsheet/[git flow] of:\n\n. Fork it\n. Create your feature branch\n(`+git checkout -b feature/my-new-feature+`)\n. Commit your changes (`+git commit -am 'Add some feature'+`)\n. Push to the branch (`+git push origin feature/my-new-feature+`)\n. Create new Pull Request\n\nEven if you can't contribute code, if you have an idea for an\nimprovement please open an\nhttps://github.com/wavesoftware/java-stringify-object/issues[issue].\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwavesoftware%2Fjava-stringify-object","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwavesoftware%2Fjava-stringify-object","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwavesoftware%2Fjava-stringify-object/lists"}