beckchr/staxon

Specifying indent and newline characters for JSON output?

Closed this issue · 5 comments

Hello,

Is there a way to control formatting characters in JSON like one can for XML?

// in XML
XMLEventWriter xmlWriter = XMLOutputFactory.newInstance().createXMLEventWriter(output);
xmlWriter = new PrettyXMLEventWriter(xmlWriter, " ", "\r\n"); // format output
xmlWriter.add(xmlReader);

But for JSON, similar code does not modify the formatting characters.

Is there a way to modify the JSON output in the same way?

For JSON output, indent and newline seem to be hardcoded in JsonStreamTargetImpl.

If not, I'd like to make this a feature request.

Thank you,

Dave

You are right, when using the default stream factory, pretty-printing cannot be customized. I'm taking this as a feature request. However, you can use the Jackson (or Gson) streaming backend and customize formatting there.
The default pretty-printer for Jackson uses two spaces and the system's line separator. If this what you want, just add the staxon-jackson jar and you're done. To adjust Jackson's indentation, do something like this:

import java.io.IOException;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.impl.Indenter;
import org.codehaus.jackson.util.DefaultPrettyPrinter;

import de.odysseus.staxon.json.JsonXMLConfig;
import de.odysseus.staxon.json.JsonXMLConfigBuilder;
import de.odysseus.staxon.json.JsonXMLOutputFactory;
import de.odysseus.staxon.json.stream.JsonStreamFactory;
import de.odysseus.staxon.json.stream.jackson.JacksonStreamFactory;

public class CustomPrettyPrinting {
  static class MyIndenter implements Indenter {
    final String indent;
    final String newline;

    public MyIndenter() {
      this("\t", System.getProperty("line.separator"));
    }  
    public MyIndenter(String indent, String newline) {
      this.indent = indent;
      this.newline = newline;
    }    
    @Override
    public boolean isInline() {
      return false;
    }
    @Override
    public void writeIndentation(JsonGenerator jg, int level) throws IOException, JsonGenerationException {
      jg.writeRaw(newline);
      for (int i = 0; i < level; i++) {
        jg.writeRaw(indent);
      }
    }
  }

  public static void main(String[] args) throws XMLStreamException {
    JsonStreamFactory streamFactory = new JacksonStreamFactory() {
      @Override
      protected JsonGenerator configure(JsonGenerator generator, boolean pretty) {
        generator = super.configure(generator, false);
        if (pretty) {
          DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
          prettyPrinter.indentArraysWith(new MyIndenter());
          prettyPrinter.indentObjectsWith(new MyIndenter());
          generator.setPrettyPrinter(prettyPrinter);
        }
        return generator;
      }
    };

    JsonXMLConfig config = new JsonXMLConfigBuilder().prettyPrint(true).build();
    JsonXMLOutputFactory outputFactory = new JsonXMLOutputFactory(config, streamFactory);

    XMLStreamWriter writer = outputFactory.createXMLStreamWriter(System.out);
    writer.writeStartDocument();
    writer.writeStartElement("alice");
    writer.writeCharacters("bob");
    writer.writeEndElement();
    writer.writeEndDocument();
    writer.close();
  }
}

Hi Christoph,

Thank you for the feedback and for making configuration of JSON formatting
a feature request.
I'm looking forward to the enhancement. It will be a plus to be able to
apply formatting parameters consistently for XML and JSON.

Temporarily, I copied JsonStreamTargetImpl and changed the two values and
then extended JsonStreamFactoryImpl to return my JsonStreamTargetImpl.

I really like what you have developed. I'm working on code to convert
between JSON and XML without a loss of data and format both JSON and XML.

I have a second feature request and (I think) some bugs to report: The
enhancement is to make the JSON parse error messages more informative like
XML parse error messages.

For instance, the XML parser provides line number and column:

xmlErr = "value";

ParseError at [row,col]:[1,39]
Message: The end-tag for element type "test" must end with a '>' delimiter.

The JSON Parser has return:

 java.io.IOException: Unexpected symbol: START_ARRAY   - when an extra

[ is encountered.

 Multiple roots within document   - when parsing valid JSON with two

elements below opening {

In other cases, invalid JSON is parsed with out error. - for instance if
missing closing ] bracket, or } instead of closing ]

I have a number of examples that I can provide.

Please let me know if I can help.

Thank you,
Dave

On Fri, Jun 15, 2012 at 2:07 AM, Christoph Beck <
reply@reply.github.com

wrote:

You are right, when using the default stream factory, pretty-printing
cannot be customized. I'm taking this as a feature request. However, you
can use the Jackson (or Gson) streaming backend and customize formatting
there.
The default pretty-printer for Jackson uses two spaces and the system's
line separator. If this what you want, just add the staxon-jackson jar
and you're done. To adjust Jackson's indentation, do something like this:

import java.io.IOException;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.impl.Indenter;
import org.codehaus.jackson.util.DefaultPrettyPrinter;

import de.odysseus.staxon.json.JsonXMLConfig;
import de.odysseus.staxon.json.JsonXMLConfigBuilder;
import de.odysseus.staxon.json.JsonXMLOutputFactory;
import de.odysseus.staxon.json.stream.JsonStreamFactory;
import de.odysseus.staxon.json.stream.jackson.JacksonStreamFactory;

public class CustomPrettyPrinting {
 static class MyIndenter implements Indenter {
   final String indent;
   final String newline;

   public MyIndenter() {
     this("\t", System.getProperty("line.separator"));
   }
   public MyIndenter(String indent, String newline) {
     this.indent = indent;
     this.newline = newline;
   }
   @Override
   public boolean isInline() {
     return false;
   }
   @Override
   public void writeIndentation(JsonGenerator jg, int level) throws
IOException, JsonGenerationException {
     jg.writeRaw(newline);
     for (int i = 0; i < level; i++) {
       jg.writeRaw(indent);
     }
   }
 }

 public static void main(String[] args) throws XMLStreamException {
   JsonStreamFactory streamFactory = new JacksonStreamFactory() {
     @Override
     protected JsonGenerator configure(JsonGenerator generator, boolean
pretty) {
       generator = super.configure(generator, false);
       if (pretty) {
         DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
         prettyPrinter.indentArraysWith(new MyIndenter());
         prettyPrinter.indentObjectsWith(new MyIndenter());
         generator.setPrettyPrinter(prettyPrinter);
       }
       return generator;
     }
   };

   JsonXMLConfig config = new
JsonXMLConfigBuilder().prettyPrint(true).build();
   JsonXMLOutputFactory outputFactory = new JsonXMLOutputFactory(config,
streamFactory);

   XMLStreamWriter writer =
outputFactory.createXMLStreamWriter(System.out);
   writer.writeStartDocument();
   writer.writeStartElement("alice");
   writer.writeCharacters("bob");
   writer.writeEndElement();
   writer.writeEndDocument();
   writer.close();
 }
}

Reply to this email directly or view it on GitHub:
#5 (comment)

Dave, would you please create separate issues?

  • Add location (row/column) to parse exception messages
  • Parser accepts invalid Json

A sample input, code snippet (or even better: unit test) demonstrating the problem would be nice.

Thanks!

Regarding multiple roots: this is possible with a "virtual root" (see wiki, virtualRoot property).

Christoph,

Yes, I will add those as separate issues with code samples and I'll try to
include unit tests.

You are probably already aware, but http://jsonlint.com/ references an
open source JSON parser/validator that provides parse error line numbers.

Thanks for the pointing me to the "virtualRoot" property. I had imagined
that would be the way to solve that multiple root issue - obviously, you've
already thought of it.

Thanks!

Dave

On Sun, Jun 24, 2012 at 8:47 AM, Christoph Beck <
reply@reply.github.com

wrote:

Dave, would you please create separate issues?

  • Add location (row/column) to parse exception messages
  • Parser accepts invalid Json

A sample input, code snippet (or even better: unit test) demonstrating the
problem would be nice.

Thanks!

Regarding multiple roots: this is possible with a "virtual root" (see
wiki, virtualRoot property).


Reply to this email directly or view it on GitHub:
#5 (comment)

Added constructor for JsonStreamFactoryImpl where you can pass prettySpace, prettyIndent and prettyNewline strings. Now you can do:

JsonXMLConfig config = new JsonXMLConfigBuilder().prettyPrint(true).build();
JsonStreamFactory streamFactory = new JsonStreamFactoryImpl(" ", "  ", "\n");
JsonXMLOutputFactory outputFactory = new JsonXMLOutputFactory(config, streamFactory);

XMLStreamWriter writer = outputFactory.createXMLStreamWriter(System.out);
writer.writeStartDocument();
writer.writeStartElement("alice");
writer.writeCharacters("bob");
writer.writeEndElement();
writer.writeEndDocument();
writer.close();