将JsonNode序列化为Jackson中的特定JSON格式

3

我有一个JsonNode结果,我想要打印出来。到目前为止,我使用的是:

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);

输出结果如下:

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

但我需要它以特定的格式呈现:
{
  "A" : [ 
    {
      "Ai" : {
        "Ai1" : 42,
        "Ai2" : 55
      }
    } 
  ],
    "B" : [
      86 
    ]
}

为了更好的理解,我正在从JSONObject转换到Jackson,所以第二个输出是由JSONObject.serialize()输出的。

另外,上面展示的每种格式都有名称吗?它们似乎遵循不同的标准。


我不确定getClass().getSimpleName()在String.format("out.json", getClass().getSimpleName())中的作用,但结果仍将是out.json。 - Nicolas Filotto
2个回答

5
你可以自定义Jackson的输出缩进方式。根据您使用的Jackson版本,有不同的实现方法。

Jackson 2.5及更高版本

请执行以下步骤:
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);

默认情况下,使用2个空格。要使用不同数量的空格,请使用DefaultIndenter构造函数,该函数接收字符串以缩进级别和换行符:

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

Jackson 2.4及早期版本

请执行以下操作:

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);

Lf2SpacesIndenter 只能使用 2 个空格,无法更改。

如果您需要不同数量的空格,您需要编写自定义实现。下面是一个使用与 Jackson 2.5 中介绍的 DefaultIndenter 相同代码的示例:

/**
 * 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);
        }
    }
}

可以按以下方式使用:

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

嘿,很抱歉修改晚了,但是我似乎在使用Jackson 2.4.4版本,而DefaultIndenter是在2.5版本中引入的。有没有不使用DefaultIndenter的解决方法? - THIS USER NEEDS HELP
@THISUSERNEEDSHELP 对于Jackson 2.4.4,请使用new DefaultPrettyPrinter.Lf2SpacesIndenter()代替new DefaultIndenter(" ", DefaultIndenter.SYS_LF)。这种方法已经在Jaythaking的答案中提到过。但是,只有当您为数组自定义缩进(printer.indentArraysWith(indenter))时,才能获得所需的结果。 - cassiomolin
抱歉再次晚发请求,但是两个空格固定了吗?我想知道我们是否可以生成3/6/9/12/...个空格的缩进。 - THIS USER NEEDS HELP
1
@THISUSERNEEDSHELP 是的,Lf2SpacesIndenter 是固定为 2 个空格。请查看我的更新答案,其中提供了一种允许您定义空格数量的方法。 - cassiomolin

2

您可以使用以下代码设置自定义的DefaultPrettyPrinter

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

看看DefaultPrettyPrinter提供的方法 这里


1
这似乎与第一个 JSON 的输出不同?也就是说,[{ 仍然在同一行。 - THIS USER NEEDS HELP
在最新版本的Jackson中,这不是正确的方法。自Jackson 2.5以来,Lf2SpacesIndenter已被弃用,并且我认为它在Jackson 2.7中已被删除。 - cassiomolin
它不会产生所需的输出。您还必须缩进数组。请查看我的答案 - cassiomolin
我提供了工具和方法。人们不应总是期望复制粘贴的解决方案。如果您能改进我的答案并使其成为您自己的,那就太好了... - Jaythaking
@THISUSERNEEDSHELP 如果你想让数组在不同的行上缩进,你需要将一个换行字符串作为属性指定给Lf2SpacesIndenter...(请参见我的更新答案) - Jaythaking
你好,是否可以使用三个空格缩进而不是两个?抱歉更新晚了。 - THIS USER NEEDS HELP

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接