View Javadoc
1   /*
2    * Copyright 2012-2025 CodeLibs Project and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.codelibs.fess.job;
17  
18  import java.util.Collection;
19  import java.util.Map;
20  import java.util.Set;
21  
22  /**
23   * Formatter that outputs index documents as JSON files.
24   */
25  public class JsonIndexExportFormatter implements IndexExportFormatter {
26  
27      /**
28       * Creates a new JsonIndexExportFormatter instance.
29       */
30      public JsonIndexExportFormatter() {
31          // default constructor
32      }
33  
34      @Override
35      public String getFileExtension() {
36          return ".json";
37      }
38  
39      @Override
40      public String getIndexFileName() {
41          return "index.json";
42      }
43  
44      @Override
45      public String format(final Map<String, Object> source, final Set<String> excludeFields) {
46          final StringBuilder json = new StringBuilder();
47          json.append("{\n");
48  
49          boolean first = true;
50          for (final Map.Entry<String, Object> entry : source.entrySet()) {
51              final String field = entry.getKey();
52              if (excludeFields.contains(field)) {
53                  continue;
54              }
55  
56              final Object value = entry.getValue();
57              if (value == null) {
58                  continue;
59              }
60  
61              if (!first) {
62                  json.append(",\n");
63              }
64              first = false;
65  
66              json.append("  \"").append(escapeJson(field)).append("\": ");
67              appendJsonValue(json, value);
68          }
69  
70          json.append("\n}\n");
71          return json.toString();
72      }
73  
74      private void appendJsonValue(final StringBuilder json, final Object value) {
75          if (value instanceof Collection) {
76              json.append("[");
77              boolean first = true;
78              for (final Object item : (Collection<?>) value) {
79                  if (!first) {
80                      json.append(", ");
81                  }
82                  first = false;
83                  appendJsonValue(json, item);
84              }
85              json.append("]");
86          } else if (value instanceof Map) {
87              json.append("{");
88              boolean first = true;
89              for (final Map.Entry<?, ?> entry : ((Map<?, ?>) value).entrySet()) {
90                  if (!first) {
91                      json.append(", ");
92                  }
93                  first = false;
94                  json.append("\"").append(escapeJson(String.valueOf(entry.getKey()))).append("\": ");
95                  appendJsonValue(json, entry.getValue());
96              }
97              json.append("}");
98          } else if (value instanceof Number) {
99              json.append(value);
100         } else if (value instanceof Boolean) {
101             json.append(value);
102         } else {
103             json.append("\"").append(escapeJson(String.valueOf(value))).append("\"");
104         }
105     }
106 
107     private String escapeJson(final String text) {
108         if (text == null || text.isEmpty()) {
109             return "";
110         }
111         final StringBuilder sb = new StringBuilder(text.length());
112         for (int i = 0; i < text.length(); i++) {
113             final char c = text.charAt(i);
114             switch (c) {
115             case '"':
116                 sb.append("\\\"");
117                 break;
118             case '\\':
119                 sb.append("\\\\");
120                 break;
121             case '\b':
122                 sb.append("\\b");
123                 break;
124             case '\f':
125                 sb.append("\\f");
126                 break;
127             case '\n':
128                 sb.append("\\n");
129                 break;
130             case '\r':
131                 sb.append("\\r");
132                 break;
133             case '\t':
134                 sb.append("\\t");
135                 break;
136             default:
137                 if (c < 0x20) {
138                     sb.append(String.format("\\u%04x", (int) c));
139                 } else {
140                     sb.append(c);
141                 }
142                 break;
143             }
144         }
145         return sb.toString();
146     }
147 }