Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use "JSR-353: Java API for JSON Processing," without Method Chaining

I wanted to share my question/answer with using the new JSR-353 which is the Java API for JSON Processing. Specifically you can manipulate JSON data in 2 different "API" the Streaming and the Object API.

If you type in "jsr-353 tutorial" into google you will get many results.

https://jcp.org/en/jsr/detail?id=353 ~ the details on the specific request.

https://jsonp.java.net/ ~ main site for the API, also links you to the Oracle Tutorial under "documentation" located here https://docs.oracle.com/javaee/7/tutorial/doc/jsonp.htm I will talk more about this tutorial later.

and finally this one

http://www.javabeat.net/java-json-api-jsr-353/

I want to talk about the last link first, as it's one which gave a lot of good detail for me to start, and one of the only real tutorials out there (there are others, but they are basically the same).

What I really tried to learn about the API from being new to not only this API but JSON in general is.

When To Use Streaming API and Object Model API?

If you want the JSON data to be written to a character stream like a file or to a byte stream then the Streaming API will be the best choice as it directly perform the writing operation into the buffer without constructing the object tree in the memory i.e there is not intermediate form generated before the final JSON data is created.

If you want to hold the JSON data as an object tree in the memory i.e not write to any stream but store the tree in the memory so that one can reuse the JSON data without need to reparse it or one can also serialize the object tree to persist/save the JSON data. Here the JSON data will be represented in the form of an object tree.

Now the Streaming API description made a lot of sense to me and I needed to save a file so this made sense to me.

As for the Object API it also made sense what it was doing, save the object so I can reuse it later in my code, awesome.

The problem is that I didn't get an answer to my question, which I will explain what exactly I am looking for now.

My Question is:

I basically have 1 object that contains other objects/arrays.

Originally I was using a BufferedWriter to writer the data to a new line into a text file.

my format looks something like this.

bw.write(1);
bw.newLine();
bw.write(2);
bw.newLine();

for(int i = 0; i < 4; i++)
{
bw.write(i);
bw.newLine();
}

bw.write(2);
bw.newLine();

for(int j = 0; j < 2; j++)
{
bw.write(j);
bw.newLine();
bw.write(j+5);
bw.newLine();
bw.write(2);
bw.newLine();
bw.write(j*4);
bw.newLine();
}

bw.write(12);
bw.newLine();

for(int k = 0; k < 82; k++)
{
bw.write(k);
bw.newLine();
bw.write(k*5);
bw.newLine();

//do some additional code here

bw.write(2);
bw.newLine();
bw.write(k*4);
bw.newLine();
}

then finish up. Granted the numbers and such are placeholders and in reality everything from the write data, to the loop amounts are variable data I read from another file.

As you can see I cannot use the traditional "method-chaining" that comes with JSR-353.

As for what method chaining is, take a look at wiki http://en.wikipedia.org/wiki/Method_chaining. An example of Method Chaining using the Streaming API would be this, as shown in the above tutorial:

FileWriter writer = new FileWriter("c:\\example.txt");
JsonGenerator gen = Json.createGenerator(writer);

gen.writeStartObject()
   .write("firstName", "Duke")
   .write("lastName", "Java")
   .write("age", 18)
   .write("street/Address", "100 Internet Dr")
   .write("city", "JavaTown")
   .write("state", "JA")
   .write("postalCode", "12345")
   .writeStartArray("phoneNumbers")
      .writeStartObject()
         .write("type", "mobile")
         .write("number", "111-111-1111")
      .writeEnd()
      .writeStartObject()
         .write("type", "home")
         .write("number", "222-222-2222")
      .writeEnd()
   .writeEnd()
.writeEnd();
gen.close();

I also looked at the Oracle Tutorial which confused me a bit where I saw "Generating/Parsing" as I was looking for a way to save files.

https://docs.oracle.com/javaee/7/tutorial/doc/jsonp001.htm

19.1.3 Generating and Parsing JSON Data

For generating and parsing JSON data, there are two programming models, which are similar to those used for XML documents.

The streaming model uses an event-based parser that reads JSON data one element at a time. The parser generates events and stops for processing when an object or an array begins or ends, when it finds a key, or when it finds a value. Each element can be processed or discarded by the application code, and then the parser proceeds to the next event. This approach is adequate for local processing, in which the processing of an element does not require information from the rest of the data. The streaming model generates JSON output to a given stream by making a function call with one element at a time.

The tutorial mentions this but it was confusing exactly what this mean, especially me thinking this was for writing and not reading. When it mentioned the last line (in bold) it didn't make much sense why it would do it one at a time, and made it seem that it only dealt with part of the object, not the whole, as the Object API mentions dealing with the entire tree.

So instead of dealing with the Streaming API, I started with the Object API. I tried saving the file at first to a FileWriter, but nothing would be saved. Eventually I switched to a StringWriter and was using that in my project. I decided to switch back to the FileWriter after finishing up my structure and it somehow saved to the file, but I realize that part of my code was cut off at the end. I tried to do a tiny structure and it would print nothing.

like image 286
XaolingBao Avatar asked Nov 09 '14 21:11

XaolingBao


1 Answers

Now to answer my question.

It was some weird bug that allowed me to write to the file using the Object API, because apparently the Object API is not meant to save any data, but just to keep it as an Object. The Streaming API's purpose is to save or send to a stream, but not be saved at all. It is nice and convenient to have both options in case we need to do either or.

After having this issue I decided to switch back to the Streaming API and it works, so I wanted to share my answer for both the Streaming API and the Object API as both have different ways to code this.

Streaming API with Method-Chaining

private static void buildJsonUsingStreamingApi() {

  //Create a StringWriter instance to buffer the JSON data.
  StringWriter writer = new StringWriter();

  //Create a JSON generator backed by the StringWriter instance created above.
  JsonGenerator generator = Json.createGenerator(writer);

  //Start building the JSON Data- Uses Method chaining technique.
  //The JSON data gets streamed in the buffer as and when the
  //different methods are invoked.
  generator.writeStartArray()
            .writeStartObject()//Indicates the start of an JSON object
              .write("parentid", 23424900)
              .write("name","Mexico City")
              .write("url", "http://where.yahooapis.com/v1/place/116545")
              .writeStartObject("placeType")//Creating a nested object i.e an JSON object withing another object
                .write("name","Town")
                .write("code", 7)
              .writeEnd()
              .write("woeid", 116545)
            .writeEnd()//Indicates the end of an JSON object
            .writeStartObject()
              .write("name","Jackson")
              .write("url", "http://where.yahooapis.com/v1/place/2428184")
              .writeStartObject("placeType")
                .write("name","Town")
                .write("code", 7)
              .writeEnd()
            .write("parentid", 23424977)
            .write("woeid", 2428184)
            .writeEnd()
          .writeEnd();//Indicates the end of the JSON array.
  //Writes the data in the buffer to the String buffer.
  generator.flush();

  //Prints the JSON data onto the console.
  System.out.println(writer.toString());
}

Output:

[
 {
   "parentid": 23424900,
   "name": "Mexico City",
   "url": "http://where.yahooapis.com/v1/place/116545",
   "placeType": {
     "name": "Town",
     "code": 7
   },
   "woeid": 116545
 },
 {
   "name": "Jackson",
   "url": "http://where.yahooapis.com/v1/place/2428184",
   "placeType": {
     "name": "Town",
     "code": 7
   },
   "parentid": 23424977,
   "woeid": 2428184
 }
]

Now as you can see straight forward approach, but I cannot do this with my application, so this is how I got it done.

Streaming API without Method-Chaining

         FileWriter fw = new FileWriter("c:\\example.txt");

        JsonGenerator gen = Json.createGenerator(fw);
        JsonGenerator mainObj = gen.writeStartObject(); //create your start object from the generator

        mainObj.write("object1", 10); //write value:key pairs as needed
        mainObj.write("object2", 1);
        mainObj.write("object3", 11);
        mainObj.write("object4", 11);
        mainObj.write("object5", 12); 

        JsonGenerator loop1 = mainObj.writeStartArray("Loop1"); //When needing to create a new 
                                                               //Array create a new start array                                                                                         
       for(int i = 0; i < 2; i++)     //based on the parent Object/Array, in this case "mainObj."
          loop1.write(5);            //could method chain

        loop1.writeEnd(); //in this case I did not need to create a new  
                          //object for each as I have only one element.

        JsonGenerator loop2 = mainObj.writeStartArray("Loop2"); //same as above to create Array.
        JsonGenerator loopObj2;                                //create new object 


        for(int i = 0; i < 9; i++)
        {

           loopObj2 = loop2.writeStartObject();         //using method-chaining with inner object
                   .write("LoopItem1",10)               //creates an object each time from loop2.
                   .write("LoopItem2",12).writeEnd();   //note method-chaining doesn't have to be 
                                                        //used here
              /*loop2.writeStartObject()                //If we switched to using this code we 
                .write("LoopItem1",10)                  //would be stuck with method-chaining.    
               .write("LoopItem2",12).writeEnd();*/     //loopObj2 isn't needed technically.

        }
        loop2.writeEnd();

        JsonGenerator loop3 = mainObj.writeStartArray("Loop3"); //same as above
        JsonGenerator loopObj3; //same as above

         for(int i = 0; i < 3; i++)
        {
             loopObj3 = loop3.writeStartObject(); //create new object from loop3.
                                                 //note this is exactly the same as above, we 
                                                //just don't use method chaining here, even                 
                                               //though we could chain the first 3



            loopObj3.write("LoopItem1", 57);
            loopObj3.write("LoopItem2", 67);
            loopObj3.write("LoopItem3", 0);
            System.out.println("Breaking Method-Chain just to do it...");
            loopObj3.write("LoopItem4", 9);
            loopObj3.writeEnd();                
        }

        loop3.writeEnd();
        mainObj.writeEnd();
        gen.close();

Output:

{  
   "object1":10,
   "object2":1,
   "object3":11,
   "object4":11,
   "object5":12,
   "Loop1":[  
      5,
      12,
      5,
      12
   ],
   "Loop2":[  
      {  
         "LoopItem1":10,
         "LoopItem2":12
      },
      {  
         "LoopItem1":10,
         "LoopItem2":12
      },
      {  
         "LoopItem1":10,
         "LoopItem2":12
      },
      {  
         "LoopItem1":10,
         "LoopItem2":12
      },
      {  
         "LoopItem1":10,
         "LoopItem2":12
      },
      {  
         "LoopItem1":10,
         "LoopItem2":12
      },
      {  
         "LoopItem1":10,
         "LoopItem2":12
      },
      {  
         "LoopItem1":10,
         "LoopItem2":12
      },
      {  
         "LoopItem1":10,
         "LoopItem2":12
      }
   ],
   "Loop3":[  
      {  
         "LoopItem1":57,
         "LoopItem2":67,
         "LoopItem3":0,
         "LoopItem4":9
      },
      {  
         "LoopItem1":57,
         "LoopItem2":67,
         "LoopItem3":0,
         "LoopItem4":9
      },
      {  
         "LoopItem1":57,
         "LoopItem2":67,
         "LoopItem3":0,
         "LoopItem4":9
      }
   ]
}

I also wanted to show how I could do loop 3 using both method-chaining and calling.

            loopObj3.write("LoopItem1", 57)
            .write("LoopItem2", 67)
            .write("LoopItem3", 0);

            System.out.println("Breaking Method-Chain just to do it...");

            loopObj3.write("LoopItem4", 9);
            loopObj3.writeEnd();  

Object API with Method-Chaining

    private static void buildJsonUsingObjectModelApi() {
  System.out.println("Json Building using Object Model API");
  JsonArray jsonArray =
          //Create an Array Builder to build an JSON Array
          Json.createArrayBuilder()
            .add(Json.createObjectBuilder()//Create an Object builder to build JSON Object
              .add("parentid", 23424900)
              .add("name","Jackson")
              .add("url", "http://where.yahooapis.com/v1/place/2428184")
              .add("placeType", Json.createObjectBuilder()//Another nested JSON Object
                    .add("name", "Town")
                    .add("code",7)
                  )
              .add("woeid", 116545)
              .build()//The JSON Object completely constructed.
            )
            .add(Json.createObjectBuilder()//Another object builder to build JSON Object.
              .add("name","Mexico City")
              .add("url", "http://where.yahooapis.com/v1/place/116545")
              .add("placeType", Json.createObjectBuilder()
                    .add("name", "Town")
                    .add("code",7)
                  )
              .add("parentid", 23424977)
              .add("woeid", 2428184)
              .build()
             )
            .build();
  StringWriter writer = new StringWriter();

  //Extracting the JSON data from the JSON object tree into the string.
  Json.createWriter(writer).writeArray(jsonArray);

  System.out.println(writer.toString());

}

Output:

    [
   {
      "parentid":23424900,
      "name":"Jackson",
      "url":"http://where.yahooapis.com/v1/place/2428184",
      "placeType":{
         "name":"Town",
         "code":7
      },
      "woeid":116545
   },
   {
      "name":"Mexico City",
      "url":"http://where.yahooapis.com/v1/place/116545",
      "placeType":{
         "name":"Town",
         "code":7
      },
      "parentid":23424977,
      "woeid":2428184
   }
]

Object API Without Method Chaining

            JsonObjectBuilder mainObj = Json.createObjectBuilder();

            mainObj.add("object1", 10);
            mainObj.add("object2", 1);
            mainObj.add("object3", 11);
            mainObj.add("object4", 11);
            mainObj.add("object5", 12); 

            JsonArrayBuilder loop1 = Json.createArrayBuilder();
            for(int i = 0; i < 2; i++)
                loop1.add(i);

            mainObj.add("Loop1", loop1);


            JsonArrayBuilder loop2 = Json.createArrayBuilder();
            for(int i = 0; i < 9; i++)
            {


                loop2.add(Json.createObjectBuilder()
                .add("LoopItem1",10)
                .add("LoopItem2",12));
            }
            mainObj.add("Loop2",loop2);




            JsonArrayBuilder loop3 = Json.createArrayBuilder();
            JsonObjectBuilder loop3Obj;
            for(int i = 0; i < 3; i++)
            {
                loop3Obj = Json.createObjectBuilder()
                .add("LoopItem1", 57)
                .add("LoopItem2", 67)
                .add("LoopItem3", 0);
                System.out.println("Breaking Method-Chain just to do it...");
                loop3Obj.add("LoopItem4", 9);


             loop3.add(loop3Obj);

            }

                mainObj.add("Loop3", loop3);

        JsonObject planObj = mainObj.build();
            StringWriter writer = new StringWriter();
            JsonWriter jwrite = Json.createWriter(writer);
            jwrite.write(planObj);
             System.out.println(planObj.toString());

Output:

   {
   "object1":10,
   "object2":1,
   "object3":11,
   "object4":11,
   "object5":12,
   "Loop1":[
      0,
      1
   ],
   "Loop2":[
      {
         "LoopItem1":10,
         "LoopItem2":12
      },
      {
         "LoopItem1":10,
         "LoopItem2":12
      },
      {
         "LoopItem1":10,
         "LoopItem2":12
      },
      {
         "LoopItem1":10,
         "LoopItem2":12
      },
      {
         "LoopItem1":10,
         "LoopItem2":12
      },
      {
         "LoopItem1":10,
         "LoopItem2":12
      },
      {
         "LoopItem1":10,
         "LoopItem2":12
      },
      {
         "LoopItem1":10,
         "LoopItem2":12
      },
      {
         "LoopItem1":10,
         "LoopItem2":12
      }
   ],
   "Loop3":[
      {
         "LoopItem1":57,
         "LoopItem2":67,
         "LoopItem3":0,
         "LoopItem4":9
      },
      {
         "LoopItem1":57,
         "LoopItem2":67,
         "LoopItem3":0,
         "LoopItem4":9
      },
      {
         "LoopItem1":57,
         "LoopItem2":67,
         "LoopItem3":0,
         "LoopItem4":9
      }
   ]
}

First 3 are chained, then I have my blocker which is just the println, and then I write another item, and then writeEnd() with individual method calls.

Now some of you might complain "but you do use method chaining in your one inner-object!!!" yes, yes I do, but as I mentioned I didn't have to, and I wanted to explain that I could do this with or without it, and even use both together to show flexibility.

I hope this helps out others. This took me a few days to learn and understand the API, so I wanted to share my findings. I also took about 3-4 hours to write this tutorial, so I hope it does get some use, and people enjoy it.

Thanks all :).

like image 116
XaolingBao Avatar answered Oct 25 '22 09:10

XaolingBao