Начнем с простой проблемы: иерархия фильтров. Существует один абстрактный класс AbstractFilter и два подкласса, RegexFilter и StringMatchFilter .
|
1
2
3
4
5
|
package bean.json.examples;public abstract class AbstractFilter { public abstract void filter();} |
Вот класс RegexFilter :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package bean.json.examples;public class RegexFilter extends AbstractFilter { private String pattern; public RegexFilter( final String pattern ) { this.pattern = pattern; } public void setPattern( final String pattern ) { this.pattern = pattern; } public String getPattern() { return pattern; } @Override public void filter() { // Do some work here }} |
А вот класс StringMatchFilter :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
package bean.json.examples;public class StringMatchFilter extends AbstractFilter { private String[] matches; private boolean caseInsensitive; public StringMatchFilter() { } public StringMatchFilter( final String[] matches, final boolean caseInsensitive ) { this.matches = matches; this.caseInsensitive = caseInsensitive; } public String[] getMatches() { return matches; } public void setCaseInsensitive( final boolean caseInsensitive ) { this.caseInsensitive = caseInsensitive; } public void setMatches( final String[] matches ) { this.matches = matches; } public boolean isCaseInsensitive() { return caseInsensitive; } @Override public void filter() { // Do some work here }} |
Ничего особенного, чистые бобы Java. А что если нам нужно сохранить список экземпляров AbstractFilter в JSON и, что более важно, восстановить этот список обратно из JSON? Следующий класс Filters демонстрирует, что я имею в виду:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package bean.json.examples;import java.util.ArrayList;import java.util.Arrays;import java.util.Collection;public class Filters { private Collection< AbstractFilter > filters = new ArrayList< AbstractFilter >(); public Filters() { } public Filters( final AbstractFilter ... filters ) { this.filters.addAll( Arrays.asList( filters ) ); } public Collection< AbstractFilter > getFilters() { return filters; } public void setFilters( final Collection< AbstractFilter > filters ) { this.filters = filters; }} |
Поскольку JSON является текстовым, независимым от платформы форматом, он не несет никакой специфической для типа информации. Благодаря потрясающему процессору JSON от Jackson это можно легко сделать. Итак, давайте добавим JSON-процессор Jackson в наш файл POM:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
<project> <modelversion> 4.0.0 </modelversion> <groupid> bean.json </groupid> <artifactid> examples </artifactid> <version> 0.0.1-SNAPSHOT </version> <packaging> jar </packaging> <properties> <project.build.sourceencoding> UTF-8 </project.build.sourceencoding> </properties> <dependencies> <dependency> <groupid> org.codehaus.jackson </groupid> <artifactid> jackson-mapper-asl </artifactid> <version> 1.9.6 </version> </dependency> </dependencies></project> |
Выполнив этот шаг, нам нужно сообщить Джексону, что мы намерены хранить информацию о типе вместе с нашими объектами в JSON, чтобы впоследствии можно было восстановить точные объекты из JSON. Несколько аннотаций на AbstractFilter делают именно это.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
import org.codehaus.jackson.annotate.JsonSubTypes;import org.codehaus.jackson.annotate.JsonSubTypes.Type;import org.codehaus.jackson.annotate.JsonTypeInfo;import org.codehaus.jackson.annotate.JsonTypeInfo.Id;@JsonTypeInfo( use = Id.NAME )@JsonSubTypes( { @Type( name = "Regex", value = RegexFilter.class ), @Type( name = "StringMatch", value = StringMatchFilter.class ) })public abstract class AbstractFilter { // ...} |
Вот и все! Следующий вспомогательный класс выполняет грязную работу по сериализации фильтров в строку и десериализации их обратно из строки, используя ObjectMapper Джексона :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package bean.json.examples;import java.io.IOException;import java.io.StringReader;import java.io.StringWriter;import org.codehaus.jackson.map.ObjectMapper;public class FilterSerializer { private final ObjectMapper mapper = new ObjectMapper(); public String serialize( final Filters filters ) { final StringWriter writer = new StringWriter(); try { mapper.writeValue( writer, filters ); return writer.toString(); } catch( final IOException ex ) { throw new RuntimeException( ex.getMessage(), ex ); } finally { try { writer.close(); } catch ( final IOException ex ) { /* Nothing to do here */ } } } public Filters deserialize( final String str ) { final StringReader reader = new StringReader( str ); try { return mapper.readValue( reader, Filters.class ); } catch( final IOException ex ) { throw new RuntimeException( ex.getMessage(), ex ); } finally { reader.close(); } }} |
Давайте посмотрим это в действии. Следующий пример кода
|
1
2
3
4
5
6
|
final String json = new FilterSerializer().serialize( new Filters( new RegexFilter( "\\d+" ), new StringMatchFilter( new String[] { "String1", "String2" }, true ) )); |
производит следующий JSON:
|
1
2
3
4
5
6
|
{ "filters": [ {"@type":"Regex","pattern":"\\d+"}, {"@type":"StringMatch","matches":["String1","String2"],"caseInsensitive":true} ]} |
Как видите, каждая запись в коллекции «filters» имеет свойство «@type», которое имеет значение, которое мы указали с помощью аннотирования класса AbstractFilter . Вызов нового FilterSerializer (). Deserialize (json) создает точно такой же экземпляр объекта Filters .
Ссылка: JSON для полиморфной сериализации Java-объектов от нашего партнера по JCG Андрея Редько в блоге Андрея Редько .