【问题标题】:JSON error when using Retrofit with HTML将 Retrofit 与 HTML 一起使用时出现 JSON 错误
【发布时间】:2017-04-02 23:03:32
【问题描述】:

当我从 index.php 中删除 HTML 代码并将 php 行保持在 .但是当我使用如下所示的 HTML 行时——我需要应用程序的其他部分——它会给出错误:

“使用 JsonReader.setLenient(true) 在第 1 行第 1 列路径 $ 处接受格式错误的 JSON”

我试过用

Gson gson = new GsonBuilder()
            .setLenient()
            .create();

Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(Constants.BASE_URL)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build();

但它给了我另一个错误:

“java.lang.IllegalStateException:应为 BEGIN_OBJECT,但在第 1 行第 1 列路径 $ 处为 STRING”

这是我的Logcat output

如何解决这个问题?!!!

FirstPOIPreference.java:

public class FirstPOIPreference extends DialogPreference {

private EditText et_alias, et_keyword, et_address;
private Spinner sp_city;
private ArrayAdapter<String> adapter;
private boolean deleted;
private Context context;
private SharedPreferences pref;
private POI poiDialog = new POI();

public FirstPOIPreference(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.context = context;
    init();
}

private void init() {
    setPersistent(false);
    setDialogLayoutResource(R.layout.pref_poi_dialog);
    setPositiveButtonText(android.R.string.ok);
    setNegativeButtonText(android.R.string.cancel);
}

@Override
protected void onBindDialogView(View view) {
    super.onBindDialogView(view);
    pref = PreferenceManager.getDefaultSharedPreferences(this.context);
    load(getSharedPreferences());
    initViews(view);
    fillViews();
}

private void load(SharedPreferences prefs) {
    poiDialog.setAlias(prefs.getString(Constants.FIRST_POI_ALIAS, ""));
    poiDialog.setKeyword(prefs.getString(Constants.FIRST_POI_KEYWORD, ""));
    poiDialog.setAddress(prefs.getString(Constants.FIRST_POI_ADDRESS, ""));
    poiDialog.setCity((prefs.getString(Constants.FIRST_POI_CITY, "")));
}

private void initViews(View view) {
    deleted = false;
    TextView tv_delete = (TextView) view.findViewById(R.id.tv_poi_delete);
    et_alias = (EditText) view.findViewById(R.id.et_poi_alias);
    et_keyword = (EditText) view.findViewById(R.id.et_poi_keyword);
    et_address = (EditText) view.findViewById(R.id.et_poi_address);
    sp_city = (Spinner) view.findViewById(R.id.spinner_poi_city);
    sp_city.setPrompt("City");
    String[] arrayCity = new String[]{"Erie", "Pittsburgh", "Cleveland", "Buffalo", "Niagara Falls", "Jamestown"};
    adapter = new ArrayAdapter <> (this.getContext(),
            android.R.layout.simple_spinner_dropdown_item, arrayCity);
    sp_city.setAdapter(adapter);

    et_alias.addTextChangedListener(new TextWatcher() {

        public void afterTextChanged(Editable s) {
            poiDialog.setAlias(et_alias.getText().toString());
        }

        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }
    });

    et_keyword.addTextChangedListener(new TextWatcher() {

        public void afterTextChanged(Editable s) {
            poiDialog.setKeyword(et_keyword.getText().toString());
        }

        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }
    });

    et_address.addTextChangedListener(new TextWatcher() {

        public void afterTextChanged(Editable s) {
            poiDialog.setAddress(et_address.getText().toString());
        }

        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }
    });

    sp_city.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView <?> parentView, View selectedItemView,
                                   int position, long id) {
            poiDialog.setCity(sp_city.getSelectedItem().toString());
        }

        @Override
        public void onNothingSelected(AdapterView <?> parentView) {
        }
    });

    tv_delete.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            AlertDialog.Builder delete_builder = new AlertDialog.Builder(context);
            delete_builder.setMessage("Are you sure you want to delete this point of interest?")
                    .setCancelable(false)
                    .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Toast.makeText(context, "Deleting POI !", Toast.LENGTH_LONG).show();
                            deletePOI();
                        }
                    })
                    .setNegativeButton("No", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.cancel();
                        }
                    });
            AlertDialog alertDelete = delete_builder.create();
            alertDelete.setTitle("Delete POI!");
            alertDelete.show();
        }
    });
}

private void fillViews() {
    et_alias.setText(poiDialog.getAlias());
    et_keyword.setText(poiDialog.getKeyword());
    et_address.setText(poiDialog.getAddress());
    sp_city.setSelection(adapter.getPosition(poiDialog.getCity()));
}

@Override
protected void onDialogClosed(boolean positiveResult) {
    if (positiveResult) {
        String alias, keyword, address, city;
        if(deleted) {
            et_alias.setText("");
            et_keyword.setText("");
            et_address.setText("");
            sp_city.setPrompt("City");
        } else {
            alias = et_alias.getText().toString();
            keyword = et_keyword.getText().toString();
            address = et_address.getText().toString();
            city = sp_city.getSelectedItem().toString();
            if (!keyword.isEmpty() && !address.isEmpty() && !city.isEmpty()) {
                Toast.makeText(context, "Saving POI !", Toast.LENGTH_LONG).show();
                addPOI(alias, keyword, address, city);
            } else {
                Toast.makeText(context, "Required Fields are empty !", Toast.LENGTH_LONG).show();
            }
        }
    }
}

private void addPOI(String alias, String keyword, String address, String city) {
    HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
    interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
    OkHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(interceptor)
            .retryOnConnectionFailure(true)
            .connectTimeout(15, TimeUnit.SECONDS)
            .build();

    Gson gson = new GsonBuilder()
            .setLenient()
            .create();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(Constants.BASE_URL)
            .client(client)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build();

    RequestInterface requestInterface = retrofit.create(RequestInterface.class);

    User user = new User();
    user.setEmail(pref.getString(Constants.USER_EMAIL, ""));

    POI poi = new POI();
    poi.setAlias(alias);
    poi.setKeyword(keyword);
    poi.setAddress(address);
    poi.setCity(city);

    ServerRequest request = new ServerRequest();
    request.setOperation(Constants.ADD_POI);
    request.setUser(user);
    request.setPoi(poi);

    Call <ServerResponse> response = requestInterface.operation(request);
    response.enqueue(new Callback <ServerResponse>() {
        @Override
        public void onResponse(Call <ServerResponse> call,
                               retrofit2.Response <ServerResponse> response) {
            ServerResponse resp = response.body();
            if(resp.getResult().equals(Constants.SUCCESS)) {
                Toast.makeText(context, resp.getMessage(), Toast.LENGTH_LONG).show();
                SharedPreferences.Editor editor = getEditor();
                save(editor);
            } else {
                Toast.makeText(context, resp.getMessage(), Toast.LENGTH_LONG).show();
            }
        }

        @Override
        public void onFailure(Call <ServerResponse> call, Throwable t) {
            Log.d(Constants.TAG, "failed");
            Toast.makeText(context, t.getLocalizedMessage(), Toast.LENGTH_LONG).show();
        }
    });
}

private void save(SharedPreferences.Editor editor) {
    editor.putString(Constants.FIRST_POI_ALIAS, poiDialog.getAlias());
    editor.putString(Constants.FIRST_POI_KEYWORD, poiDialog.getKeyword());
    editor.putString(Constants.FIRST_POI_ADDRESS, poiDialog.getAddress());
    editor.putString(Constants.FIRST_POI_CITY, poiDialog.getCity());
    editor.apply();
}

private void deletePOI() {
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(Constants.BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    RequestInterface requestInterface = retrofit.create(RequestInterface.class);

    User user = new User();
    user.setEmail(pref.getString(Constants.USER_EMAIL, ""));

    POI poi = new POI();
    poi.setKeyword(pref.getString(Constants.FIRST_POI_KEYWORD, ""));
    poi.setCity(pref.getString(Constants.FIRST_POI_CITY, ""));

    ServerRequest request = new ServerRequest();
    request.setOperation(Constants.DELETE_POI);
    request.setUser(user);
    request.setPoi(poi);

    Call <ServerResponse> response = requestInterface.operation(request);
    response.enqueue(new Callback <ServerResponse>() {
        @Override
        public void onResponse(Call <ServerResponse> call,
                               retrofit2.Response <ServerResponse> response) {

            ServerResponse resp = response.body();
            if(resp.getResult().equals(Constants.SUCCESS)) {
                Toast.makeText(context, resp.getMessage(), Toast.LENGTH_LONG).show();
                SharedPreferences.Editor editor = getEditor();
                saveDelete(editor);
                deleted = true;
            } else {
                Toast.makeText(context, resp.getMessage(), Toast.LENGTH_LONG).show();
            }
        }

        @Override
        public void onFailure(Call <ServerResponse> call, Throwable t) {
            Log.d(Constants.TAG, "failed");
            Toast.makeText(context, t.getLocalizedMessage(), Toast.LENGTH_LONG).show();
        }
    });
}

private void saveDelete(SharedPreferences.Editor editor) {
    editor.putString(Constants.FIRST_POI_ALIAS, "");
    editor.putString(Constants.FIRST_POI_KEYWORD, "");
    editor.putString(Constants.FIRST_POI_ADDRESS, "");
    editor.putString(Constants.FIRST_POI_CITY, "");
    editor.apply();
}

}

index.php:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <pre>
        <?php

        // dispaly all errors except Warnings
        ini_set('display_errors',1);
        error_reporting(E_ALL & ~E_WARNING);

        include 'fetch_data.php';
        require_once 'users/login.php';
        require_once 'preferences/preferences.php';
        require_once 'include/functions_validation.php';

        $login = new Login();
        $pref = new Preferences();
        $fun = new FunctionsValidation();

        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
            $data = json_decode(file_get_contents("php://input"));

            if(isset($data -> operation)) {
                $operation = $data -> operation;
                if(!empty($operation)) {
                    /*
                    * User registration and login operations
                    */
                    if($operation == 'register') {
                        echo $login -> register($data);
                    }
                    else if ($operation == 'regVerify') {
                        echo $login -> registerVerify($data);
                    }
                    else if ($operation == 'login') {
                        echo $login -> login($data);
                    }
                    else if ($operation == 'chgPass') {
                        echo $login -> changePassword($data);
                    }
                    else if ($operation == 'resPassReq') {
                        echo $login -> resetPasswordReq($data);
                    }
                    else if ($operation == 'resPass') {
                        echo $login -> resetPassword($data);
                    }
                    /*
                     * Point of Interest operations
                     */
                    else if($operation == 'addPOI') {
                        echo $pref -> addPOI($data);
                    }
                    else if($operation == 'deletePOI') {
                        echo $pref -> deletePOI($data);
                    }

                    /**
                     * Mileage Distance operations
                     */
                    else if($operation == 'addMileage') {
                        echo $pref -> addMileage($data);
                    }
                    else if($operation == 'deleteMileage') {
                        echo $pref -> deleteMileage($data);
                    }
                } else { // if operation is empty
                    echo $fun -> getMsgParamNotEmpty();
                }
            } else { // if operation is not set
                echo $fun -> getMsgInvalidParam();
            }
        } else if ($_SERVER['REQUEST_METHOD'] == 'GET') {
            echo "GeolocationNews API";
        }
        ?>
      </pre>
  </body>
</html>

【问题讨论】:

    标签: php android html json retrofit


    【解决方案1】:

    Constants.BASE_URL 是否指向您的 index.php 页面?

    如果是这样,您尝试将 HTML 页面解析为 JSON,这是无效的。我建议您从您的 Android 应用程序中发送某种可以从 PHP 中检查的查询参数,这样 HTML 部分就不会包含在输出中。

    例如,假设您的 Android 应用将请求提交给 index.php?output=json。然后在你的 index.php 中你可以检查一下:

    <?php
    $jsonOnly = (isset($_GET['output']) && $_GET['output'] == 'json');
    
    if (!$jsonOnly):
    ?>
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <pre>
    <?php
    endif;
    
    // your existing PHP code here
    
    if (!$jsonOnly):
    ?>
        </pre>
    </body>
    </html>
    <?php
    endif;
    ?>
    

    编辑 但是,我认为将逻辑与演示文稿分开会更好。如果您要将所有 PHP 代码放在一个单独的文件中,例如handle_request.php?然后在您的 index.php 中,您可以拥有所有 HTML 代码,然后包含该文件:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <pre>
            <?php include('handle_request.php'); ?>
        </pre>
    </body>
    </html>
    

    然后,您可以为您的应用创建一个不同的文件,用作Constants.BASE_URL,例如app.php,它只设置正确的 JSON 内容类型标头并包含相同的文件:

    <?php
    header('Content-Type: application/json');
    include('handle_request.php');
    ?>
    

    【讨论】:

    • 是的,BASE_URL 指向 index.php。你在我的index.php中说的我已经做了,但是我不知道如何发送参数。我已经编辑了我的问题并发布了 Logcat 输出的图像,就像你说的那样。
    • 我对使用 Retrofit 不是很熟悉,但从 Google 告诉我的情况来看,通常有一个接口可以定义 API 端点。在那里,您可以使用 @Query() 注释发送查询参数(只需将其添加到您的基本 url 将不起作用)。尽管我认为您可以从两个位置访问此代码,其中一个需要 JSON,另一个需要 HTML。在我看来,将逻辑与表示分开会更好,所以我将更新我的答案。
    猜你喜欢
    • 2017-05-09
    • 1970-01-01
    • 2010-11-16
    • 1970-01-01
    • 2021-06-25
    • 2018-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多