Serialize JsonNode to a very specific JSON format in Jackson

只愿长相守 提交于 2019-12-22 14:38:13

问题


I have JsonNode result that I want to print out. So far, I am using:

ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
File outputFile = new File(
    getCurOutputDir(), String.format("out.json", getClass().getSimpleName())
);
mapper.writeValue(new FileOutputStream(outputFile), resultNode);

which outputs something like:

{
  "A" : [ {
    "Ai" : {
      "Ai1" : 42,
      "Ai2" : 55
    }
  } ],
    "B" : [ 86 ]
}

but I need it to be in this specific format:

{
  "A" : [ 
    {
      "Ai" : {
        "Ai1" : 42,
        "Ai2" : 55
      }
    } 
  ],
    "B" : [
      86 
    ]
}

For context, I am transitioning from JSONObject to Jackson, so the second output is the one that is outputted by JSONObject.serialize().

Also, is there a name for each of the format presented above? It seems like it abides by different standards.


回答1:


You can customize how Jackson will indent the output. There are different ways to achieve it, according to the Jackson version you are using.

Jackson 2.5 and newer versions

Do the following:

DefaultPrettyPrinter printer = new DefaultPrettyPrinter();
Indenter indenter = new DefaultIndenter();
printer.indentObjectsWith(indenter); // Indent JSON objects
printer.indentArraysWith(indenter);  // Indent JSON arrays

ObjectMapper mapper = new ObjectMapper();
mapper.writer(printer).writeValue(new FileOutputStream(outputFile), node);

By default, 2 spaces will be used. For a different number of spaces, use the DefaultIndenter constructor that receives a string to indent levels and the line separator:

Indenter indenter = new DefaultIndenter("      ", DefaultIndenter.SYS_LF);

Jackson 2.4 and older versions

Do the following:

DefaultPrettyPrinter printer = new DefaultPrettyPrinter();
Indenter indenter = new Lf2SpacesIndenter();
printer.indentObjectsWith(indenter); // Indent JSON objects
printer.indentArraysWith(indenter);  // Indent JSON arrays

ObjectMapper mapper = new ObjectMapper();
mapper.writer(printer).writeValue(new FileOutputStream(outputFile), node);

The Lf2SpacesIndenter is limited to 2 spaces and you cannot change it.

If you need a different number of spaces, you need to write your custom implementation. Here's one that uses the same code as the DefaultIndenter introduced in Jackson 2.5:

/**
 * Default linefeed-based indenter.
 */
public class CustomSpaceIndenter extends DefaultPrettyPrinter.NopIndenter {

    public final static String SYS_LF;
    static {
        String lf;
        try {
            lf = System.getProperty("line.separator");
        } catch (Throwable t) {
            lf = "\n"; // fallback when security manager denies access
        }
        SYS_LF = lf;
    }

    public static final CustomSpaceIndenter SYSTEM_LINEFEED_INSTANCE = 
            new CustomSpaceIndenter("  ", SYS_LF);

    /**
     * We expect to rarely get indentation deeper than this number of levels,
     * and try not to pre-generate more indentations than needed.
     */
    private final static int INDENT_LEVELS = 16;
    private final char[] indents;
    private final int charsPerLevel;
    private final String eol;

    /**
     * Indent with two spaces and the system's default line feed
     */
    public CustomSpaceIndenter() {
        this("  ", SYS_LF);
    }

    /**
     * Create an indenter which uses the <code>indent</code> string to indent one level
     *  and the <code>eol</code> string to separate lines.
     */
    public CustomSpaceIndenter(String indent, String eol)  {
        charsPerLevel = indent.length();
        indents = new char[indent.length() * INDENT_LEVELS];
        int offset = 0;
        for (int i=0; i<INDENT_LEVELS; i++) {
            indent.getChars(0, indent.length(), indents, offset);
            offset += indent.length();
        }
        this.eol = eol;
    }

    public CustomSpaceIndenter withLinefeed(String lf) {
        if (lf.equals(eol)) {
            return this;
        }
        return new CustomSpaceIndenter(getIndent(), lf);
    }

    public CustomSpaceIndenter withIndent(String indent) {
        if (indent.equals(getIndent())) {
            return this;
        }
        return new CustomSpaceIndenter(indent, eol);
    }

    public String getEol() {
        return eol;
    }

    public String getIndent() {
        return new String(indents, 0, charsPerLevel);
    }

    @Override
    public boolean isInline() { 
        return false;
    }

    @Override
    public void writeIndentation(JsonGenerator jg, int level) throws IOException {
        jg.writeRaw(eol);
        if (level > 0) { // should we err on negative values (as there's some flaw?)
            level *= charsPerLevel;
            while (level > indents.length) { // unlike to happen but just in case
                jg.writeRaw(indents, 0, indents.length); 
                level -= indents.length;
            }
            jg.writeRaw(indents, 0, level);
        }
    }
}

It can be used as following:

Indenter indenter = new CustomSpaceIndenter("      ", CustomSpaceIndenter.SYS_LF);



回答2:


You can setup a custom DefaultPrettyPrinter using this:

DefaultPrettyPrinter pp = new DefaultPrettyPrinter();
pp.indentObjectsWith(new Lf2SpacesIndenter());
pp.indentArraysWith(new Lf2SpacesIndenter("\r\n"));
mapper.writer(pp).writeValue(new FileOutputStream(outputFile), resultNode);

Take a look at the method provided by DefaultPrettyPrinter HERE



来源:https://stackoverflow.com/questions/39475967/serialize-jsonnode-to-a-very-specific-json-format-in-jackson

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!