S.Jay
S.Jay

Reputation: 7

POSTing JSON object of array of JSON objects to server

I am trying to send a JSON Object of array of JSON objects from my android app to server, but it doesen't work. I have tried it using POSTMAN and it works. The following are the codes i have tried and the json format.

Here is the JSON Format

{
    "products": [
        {
            "product_id": 1,
            "product_name": "Smart Watch",
            "product_price": 99.99
        },

        {
            "product_id": 2,
            "product_name": "Smart TV",
            "product_price": 999.99
        }
    ]
}

Here is POJO Class

import com.google.gson.annotations.SerializedName;

public class Product {

    @SerializedName("product_id")
        public int productId;
        @SerializedName("product_name")
        public String productName;
        @SerializedName("product_price")
        public double productPrice;



    public Product(Integer productId, String productName, Float productPrice) {
        this.productId = productId;
        this.productName = productName;
        this.productPrice = productPrice;

    }

    public Integer getProductId() {
        return productId;
    }

    public void setProductId(Integer productId){
        this.productId = productId;
    }

    public String getproductName() {
        return productName;
    }

    public void setproductName(String productName) {
        this.productName = productName;
    }

    public String getproductPrice() {
        return productPrice;
    }

    public void setproductPrice(String productPrice) {
        this.productPrice = productPrice;
    }


}

Here is my ServiceGenerator Class

public class ServiceGenerator {

    private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();

    public static <S> S createService(Class<S> serviceClass, String baseUrl)
    {
        Retrofit builder = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create())
                .client(httpClient.build())
                .build();

        return builder.create(serviceClass);
    }
}

Added another class Customers

public class Customers {
    @SerializedName("customers")
    public List<Customer> customers;

    public List<Customer> getCustomers() {
        return customers;
    }

    public void setCustomers(List<Customer> customers) {
        this.customers = customers;
    }
}

Here is my Interface Class

public interface IRetrofit {
    @Headers({

            "Accept: application/json",
            "Content-Type: application/json"
    })
    @POST("addproduct")
    Call<Products> postRawJSON(@Body Products products);


}

and My MainActivity Class

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public void onPostClicked(View view){

        // Using the Retrofit
        IRetrofit jsonPostService = ServiceGenerator.createService(IRetrofit.class, "http://192.168.122.1/productmanager/products/");


        Product product = new Products(null, "Samsung Galaxy A5", 234.54);
        Call<Product> call = jsonPostService.postRawJSON(product);
        call.enqueue(new Callback<Product>() {

            @Override
            public void onResponse(Call<Product> call, Response<Product> response) {
                try{
                    Log.e("response-success", response.body().toString());
                }catch (Exception e){
                    e.printStackTrace();
                }

            }

            @Override
            public void onFailure(Call<Product> call, Throwable t) {
                Log.e("response-failure", call.toString());
            }
        });
    }
}

I have called the onPostClicked() method from the layout android:OnClicked property.

I have also edited the AnroidManifest File and added

<uses-permission android:name="android.permission.INTERNET" />

and am connected to the same network.

Using POSTMAN i tried sending this and it works(adds it to the db)

{
    "products": [
        {
            "product_name": "Galaxy A2",
            "product_price": 599.99
        }


    ]
}

and when i send a GET Request it also shows me it has added the new record

{
    "products": [
        {
            "product_id": 1,
            "product_name": "Smart Watch",
            "product_price": 99.99
        },

        {
            "product_id": 2,
            "product_name": "Smart TV",
            "product_price": 999.99
        },
 {
                "product_id": 3,
                "product_name": "Galaxy A2",
                "product_price": 599.99
            }

    ]
}

Here is my Log

I/TAG: --> POST http://192.168.122.1/productmanager/products/ http/1.1
       Content-Type: application/json
       Content-Length: 249
       Accept: application/json
I/TAG: {"product_id":"","product_name":"Samsung Galaxy A5","product_price":234.5}
       --> END POST (249-byte body)
I/TAG: <-- 200 OK http://192.168.122.1/productmanager/products/addcproduct (266ms)
       Date: Fri, 28 Sep 2018 12:29:59 GMT
       Server: Apache/2.4.34 (Win32) OpenSSL/1.0.2o PHP/5.6.37
       X-Powered-By: PHP/5.6.37
       Keep-Alive: timeout=5, max=100
       Connection: Keep-Alive
       Transfer-Encoding: chunked
       Content-Type: text/html; charset=UTF-8
I/TAG: <pre class="cake-error"><a href="javascript:void(0);" onclick="document.getElementById('cakeErr5bae1ec79e019-trace').style.display = (document.getElementById('cakeErr5bae1ec79e019-trace').style.display == 'none' ? '' : 'none');"><b>Notice</b> (8)</a>: Undefined index: products [<b>APP/Controller\productsController.php</b>, line <b>77</b>]<div id="cakeErr5bae1ec79e019-trace" class="cake-stack-trace" style="display: none;"><a href="javascript:void(0);" onclick="document.getElementById('cakeErr5bae1ec79e019-code').style.display = (document.getElementById('cakeErr5bae1ec79e019-code').style.display == 'none' ? '' : 'none')">Code</a> <a href="javascript:void(0);" onclick="document.getElementById('cakeErr5bae1ec79e019-context').style.display = (document.getElementById('cakeErr5bae1ec79e019-context').style.display == 'none' ? '' : 'none')">Context</a><pre id="cakeErr5bae1ec79e019-code" class="cake-code-dump" style="display: none;"><code><span style="color: #000000"><span style="color: #0000BB">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$product&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">$productTable</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">newEntity</span><span style="color: #007700">();</span></span></code>
       <span class="code-highlight"><code><span style="color: #000000"><span style="color: #0000BB">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$products&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">request</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">data</span><span style="color: #007700">[</span><span style="color: #DD0000">'products'</span><span style="color: #007700">];</span></span></code></span>
       <code><span style="color: #000000"><span style="color: #0000BB">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #007700">if&nbsp;(</span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">request</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">is</span><span style="color: #007700">(</span><span style="color: #DD0000">'post'</span><span style="color: #007700">))&nbsp;{</span></span></code></pre><pre id="cakeErr5bae1ec79e019-context" class="cake-context" style="display: none;">$res = []
       $productTable = object(App\Model\Table\ProductsTable) {

        &#039;registryAlias&#039; =&gt; &#039;products&#039;,
        &#039;table&#039; =&gt; &#039;products&#039;,
        &#039;alias&#039; =&gt; &#039;products&#039;,
        &#039;entityClass&#039; =&gt; &#039;App\Model\Entity\product&#039;,
        &#039;associations&#039; =&gt; [],
        &#039;behaviors&#039; =&gt; [],
        &#039;defaultConnection&#039; =&gt; &#039;default&#039;,
        &#039;connectionName&#039; =&gt; &#039;default&#039;

       }
       $product = object(App\Model\Entity\product) {

        &#039;[new]&#039; =&gt; true,
        &#039;[accessible]&#039; =&gt; [
            &#039;product_id&#039; =&gt; true,
            &#039;product_name&#039; =&gt; true,
            &#039;product_price&#039; =&gt; true,

        ],
        &#039;[dirty]&#039; =&gt; [],
        &#039;[original]&#039; =&gt; [],
        &#039;[virtual]&#039; =&gt; [],
        &#039;[errors]&#039; =&gt; [],
        &#039;[invalid]&#039; =&gt; [],
        &#039;[repository]&#039; =&gt; &#039;products&#039;

       }</pre><pre class="stack-trace">App\Controller\productsController::addproduct() - APP/Controller\ProductsController.php, line 77
       Cake\Controller\Controller::invokeAction() - CORE\src\Controller\Controller.php, line 440
       Cake\Http\ActionDispatcher::_invoke() - CORE\src\Http\ActionDispatcher.php, line 119
       Cake\Http\ActionDispatcher::dispatch() - CORE\src\Http\ActionDispatcher.php, line 93
       Cak
       <-- END HTTP (22852-byte body)
E/response-failure: retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall@4958691

Please help. Thank You in advance

Upvotes: 0

Views: 567

Answers (3)

Sarath Kn
Sarath Kn

Reputation: 2735

You need to pass a List of Product as @Body. Which means your Request model is an Object which contains a List of Product objects

Try creating the POJO classes like this

        public class Product {
            @SerializedName("product_id")
            public int productId;
            @SerializedName("product_name")
            public String productName;
            @SerializedName("product_price")
            public double productPrice;
        }

        public class ProductRequest {
            @SerializedName("products")
            public List<Product> products;
        }

Note: Add setters and getters if you are making fields private

Then in your retrofit interface

     Call<JsonObject> postRawJSON(@Body ProductRequest products);

Also, Instead of JsonObject use the POJO class for your response JSON structure

Update: Do the following steps

1) Create the ProductRequest model like this

    ProductRequest productRequest = new ProductRequest();
    ArrayList<Product> productList = new ArrayList<>();
    Product product = new Product(1, "Samsung Galaxy A5", 234.54);
    productList.add(product);
    productRequest.products = productList;

2) Create a POJO class for the response JSON of this API. For example create a ProductResponse class. Since I don't know your response structure, I assume it would be something like

{
  "code" : 200 , 
  "status" : "Success"
}

So for the above Response , the POJO class you have to create as follows

 public class ProductResponse {
    @SerializedName("code")
    public int code;
    @SerializedName("status")
    public String status;
}

3) Create your IRetrofit interface like this

    public interface IRetrofit {
    @Headers({
            "Accept: application/json",
            "Content-Type: application/json"
    })
    @POST("addproduct")
    Call<ProductResponse> addProducts(@Body ProductRequest productRequest);

4) Now in your Activity following is the full code to create the Request (as shown Step 1) and call the API

    ProductRequest productRequest = new ProductRequest();
    ArrayList<Product> productList = new ArrayList<>();
    Product product = new Product(1, "Samsung Galaxy A5", 234.54);
    productList.add(product);
    productRequest.products = productList;

    Call<ProductResponse> productResponseCall = jsonPostService.addProducts(productRequest);
    productResponseCall.enqueue(new Callback<ProductResponse>() {
        @Override
        public void onResponse(Call<ProductResponse> call, Response<ProductResponse> response) {
            ProductResponse productResponse = response.body(); // this  your result

        }

        @Override
        public void onFailure(Call<ProductResponse> call, Throwable t) {
            Log.e("response-failure", call.toString());
        }
    });

Upvotes: 2

Shashanth
Shashanth

Reputation: 5190

As I can see from your JSON sample, server accepts JSONObject which contains JSONArray of products

You tested it using Postman with same parameters as your server accepts. But, in your application you're preparing an object of Product class and passing it to API request. So, the final payload will be like below,

{
    "product_id": 1,
    "product_name": "Smart Watch",
    "product_price": 99.99
}

Just a JSONObject of products instead of products JSONArray.

Follow what @Srt suggested in his answer. That's correct way of sending data to your API (as per the requirement)

And also I came across your comments posted under other answers here. In one of them you said

There is no error, it just doesn't work. It doesn't add the new record – S.Jay

To check what's happening while Retrofit executing network requests, you need to add OkHttpLoggingInterceptor which logs every single request (including errors) being executed through Retrofit library.

Add this dependency to your build.gradle (app level) and sync the project

implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0'

After syncing the project add below code into ServiceGenerator class

import okhttp3.logging.HttpLoggingInterceptor;
import android.util.Log;

public class ServiceGenerator {

private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();

public static <S> S createService(Class<S> serviceClass, String baseUrl) {

    // add logging interceptor to OkHttpClient
     httpClient.addInterceptor(getInterceptor());

    Retrofit builder = new Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .client(httpClient.build())
            .build();

      return builder.create(serviceClass);
    }

    public static HttpLoggingInterceptor getInterceptor() {
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
           @Override
           public void log(String message) {
               Log.i("TAG", message);
           }
        });
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        return interceptor;
    }
}

Just add HttpLoggingInterceptor to OkHttpClient like I mentioned in above code. (or copy and paste the code)

Now, that should log every single request.

Upvotes: 0

Zun
Zun

Reputation: 1690

Your server requires you to send a list of Products, in your Android app you are sending 1 product (1 Java model). What you should do is send a list of models.

You could have avoided this problem if you gave your classes better names

Products products = new Products(null, "Samsung Galaxy A5", 234.54);

is bad because you are not creating products but a product.

Upvotes: 0

Related Questions