Silencio is a Java library for transforming and converting formats such as
using well defined API. It is applicable for most of operations such as:
- obfuscation
- minimisation (eg anonymization, minifying)
- transformation (eg encryption)
It is built from processors that manage transformations of the files (XML, JSON, Properties, etc.) decisions which decide which elements should be converted and converters that changes old value into new one.
As presented in tests this is quite easy to convert the file:
Reader input = new FileReader("myStructure.json");
Writer output = new StringWriter();
Processor processor = new Builder(Format.JSON).with(Builder.NUMBER_SEQUENCE).build();
processor.load(input);
processor.process();
processor.write(output);
System.out.println(output);
and as the result you may expect (depends of converter you selected) following output:
| suv.json -->> | < BlankConverter > | < NumberSequenceConverter > | < YourConverter> | |==========================================|===============================|=============================|===================| | { | { | { | | | "model" : "SUV", | "model" : "", | "model" : 1, | | | "wheels" : 4, | "wheels" : "", | "wheels" : 2, | | | "size" : { | "size" : { | "size" : { | | | "length" : 4.45, | "length" : "", | "length" : 3, | create your | | "height" : 2.05, | "height" : "", | "height" : 4, | | | "width" : 2.200 | "width" : "" | "width" : 5 | own | | }, | }, | }, | converter | | "weight" : { | "weight" : { | "weight" : { | | | "value" : 2.200, | "value" : "", | "value" : 5, | that's pretty | | "unit" : "ton" | "unit" : "" | "unit" : 6 | | | }, | }, | }, | | | "accessories" : [ "sunroof", "radio" ], | "accessories" : [ "", "" ], | "accessories" : [ 7, 8 ], | easy! | | "fuel" : [ { | "fuel" : [ { | "fuel" : [ { | | | "diesel" : true, | "diesel" : "", | "diesel" : 9, | | | "LPG" : true | "LPG" : "" | "LPG" : 9 | | | } ], | } ], | } ], | | | "notes" : null, | "notes" : "", | "notes" : 0, | | | "alarms" : { }, | "alarms" : { }, | "alarms" : { }, | | | "assistance" : [ ] | "assistance" : [ ] | "assistance" : [ ] | | | } | } | } | | |==========================================|===============================|=============================|===================|
So you know how it works but you want to decide which nodes should be transfered into what values. Imagine you are renting cars and your partner asked you to share all your specifications. Sounds good but you don't want to share prices (sensitive information). Also your database is a little bit outdated because all your cars already have sunroof even your services provide different information.
Take a look at example below. There are two iterations on the same file. First removes all values that match to given key money|cash|price
. Second validates key (sunroot
) and value (Optional
) and transfers values into new one ([Standard]
).
Builder builder = new Builder(Format.PROPERTIES);
builder.with(new MatcherDecision(".*(money|cash|price).*", null), Builder.BLANK)
.with(new MatcherDecision(".*sunroof.*", ".*Optional.*"), new StringConverter("[Standard]"));
Processor processor = builder.build();
processor.load(input);
processor.process();
processor.write(output);
and the conversion will produce following outcome:
| suv.properties -->> | [ removing price ] | [ updated sunroof ] | |================================|================================|================================| | model=SUV | model=SUV | model=SUV | | price.value=38504 | price.value= | price.value= | | price.currency=$ | price.currency= | price.currency= | | wheels=4 | wheels=4 | wheels=4 | | size.length=4.45 | size.length=4.45 | size.length=4.45 | | size.height=2.05 | size.height=2.05 | size.height=2.05 | | size.width=2.200 | size.width=2.200 | size.width=2.200 | | weight.value=2.200 | weight.value=2.200 | weight.value=2.200 | | weight.unit=ton | weight.unit=ton | weight.unit=ton | | accessories.sunroof=[Optional] | accessories.sunroof=[Optional] | accessories.sunroof=[Standard] | | accessories.radio=yes | accessories.radio=yes | accessories.radio=yes | | fuel.diesel=true | fuel.diesel=true | fuel.diesel=true | | fuel.LPG=true | fuel.LPG=true | fuel.LPG=true | | notes=null | notes=null | notes=null | | alarms= | alarms= | alarms= | | assistance= | assistance= | assistance= | |================================|================================|================================|
Add a maven dependency (using version from above shield) to your pom.xml
or build.gradle
:
<dependency>
<groupId>pl.damianszczepanik</groupId>
<artifactId>silencio</artifactId>
<version>2.0.0</version> <!-- check above shield to confirm last release -->
</dependency>
The simplest way to understand how to use processor with set of converters is to check tests that validate this implementation.
Both processors and converters can be extended. They are like plugins: you can add your own implementation as long as you keep the contract. There are several built-in converters that can be easily accessed via builder. Nevertheless if you need to create your own just implement convert()
method and provide the algorithm you expect. Sometimes rules might be very simple.
Silencio supports most of the popular data formats but it is possible to write support for new one. It is not difficult when using external library that allows to manipulate such format. For more reference check pull that introduced support for XML.
Once you developed your new processor or converter you should test it by providing several unit tests and by passing it to processor checker that makes basic tests using edge cases.
When you implement new processor or converter you are more than welcome to pull this change and make someone happier!
Mind that this project has several metrics that measure code quality. From continuous integration, code coverage and checkstyle. All PRs are welcome but try to keep high code coverage and good documentation of the code you deliver.