意外字符('%'(代码37)):预期有效值(数字,字符串,数组,对象,'true','false'或'null')

时间:2017-09-19 05:14:19

标签: java android web-services

有几天我试图弄清楚这个错误意味着什么。 我尝试从Web服务下载文件时收到错误。

完整错误是:

java.lang.RuntimeException: org.codehaus.jackson.JsonParseException: Unexpected character ('%' (code 37)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
at [Source: java.io.StringReader@39494c1; line: 1, column: 2]".

从Web服务执行序列化和反序列化工作的类:

public class WSClass{
public String authenticationToken;

public enum HTTPMethod {
    GET, PUT, POST, DELETE
}

// Constructor.
public WSClass() {
}

/**
 * Calls a REST endpoint in the specified URL and returns
 * the return value. Optionally deserializes it from JSON.
 *
 * @param <T>      The type of the return value
 * @param //strUrl URL relative to the applet root
 * @param method   HTTP Method used in the request
 * @param body     Body serialized for the JSON
 * @param output   The type of the return value
 * @return The REST response as an instance of type T
 */
public <T> T doMethod(String strUrl, HTTPMethod method, Object body,
                      Class<T> output) throws IOException {
    return doMethod(strUrl, method, body, output, null);
}

/**
 * Calls a REST endpoint in the specified URL and returns
 * the return value. Optionally deserializes it from JSON.
 *
 * @param <T>      The type of the return value
 * @param //strUrl URL relative to the applet root
 * @param method   HTTP Method used in the request
 * @param body     Body serialized for the JSON
 * @param output   The type of the return value
 * @param headers  Key-Value list of additional headers.
 * @return The REST response as an instance of type T
 */
@SuppressWarnings("unchecked")
public <T> T doMethod(String strUrl, HTTPMethod method, Object body,
                      Class<T> output, Map<String, String> headers) throws IOException {

    // Strip the first '/' away if it exists.
    if (strUrl.startsWith("/")) {
        strUrl = strUrl.substring(1);
    }

    // Calculate the real url based on method. IIS supports only the
    // GET and POST in default mode so we'll use the _method parameter
    // that MFWS understands.
    if (method != HTTPMethod.GET && method != HTTPMethod.POST) {
        String methodParam;
        if (strUrl.contains("?")) {
            methodParam = "&_method=";
        } else {
            methodParam = "?_method=";
        }
        strUrl += methodParam + method.name();
        method = HTTPMethod.POST;
    }

    // Initialize JSON (de)serializer.
    ObjectMapper om = new ObjectMapper();
    om.configure(org.codehaus.jackson.map.SerializationConfig.Feature.CAN_OVERRIDE_ACCESS_MODIFIERS, false);
    om.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.CAN_OVERRIDE_ACCESS_MODIFIERS, false);
    om.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    // Get URL to REST interface.
    URL u = new URL(strUrl);


    // Perform the request.
    HttpURLConnection conn = null;
    OutputStream os = null;
    InputStream is = null;
    BufferedReader in = null;
    try {

        // Open connection.
        conn = (HttpURLConnection) u.openConnection();

        // Prevent the use of cache.
        // The applet does not seem to respect the cache control flags it receives from the server.
        // For example it won't necessarily make a new request to the server even if the requested
        // resources has expired. See issue: #9234.
        conn.setUseCaches(false);

        // Set the request properties.
        conn.setRequestMethod(method.name());
        if (body != null)
            conn.setDoOutput(true);
        if (!output.equals(void.class))
            conn.setDoInput(true);

        conn.setRequestProperty("Accept", "application/json");
        conn.setRequestProperty("X-Authentication", authenticationToken);

        if (headers != null) {
            for (Map.Entry<String, String> header : headers.entrySet()) {
                System.out.println("Setting header " + header.getKey());
                conn.setRequestProperty(header.getKey(), header.getValue());
            }
        }

        // If there is a body, serialize it to the output stream.
        if (body != null) {

            os = conn.getOutputStream();
            om.writeValue(os, body);

        } else if (method != HTTPMethod.GET) {

            // No body available.
            conn.setRequestProperty("Content-Length", "0");
        }

        // Check if the caller wanted the connection as the return value.
        if (output.equals(HttpURLConnection.class)) {
            // Change ownership so we don't disconnect the connection in
            // finalize block.
            HttpURLConnection connDetached = conn;
            conn = null;
            return (T) connDetached;
        }

        // Write the output if we had output.
        if (os != null) {
            os.flush();
            os.close();
        }
        os = null;

        // Get response to input stream.
        conn.connect();
        is = conn.getInputStream();

        int contentLength = conn.getContentLength();
        if (output.equals(InputStream.class)) {
            // If the output type is input stream, just return it
            // as it is.
            InputStream isReturn = is;
            is = null;  // Change ownership.
            return (T) isReturn;
        }
        else {

            // Deserialize from JSON object.
            String response = readStringFromStream(is, contentLength);

            // Read the return value from the response.
            if (output.equals(void.class) || response.length() == 0)
                return null;
            else
                return om.readValue(response, output);

        }  // end-if (output.equals(InputStream.class))

    } catch (IOException e3) {
        throw new RuntimeException(e3);
    } finally {
        // Close streams.
        closeStream(os);
        closeStream(is);
        closeStream(in);

        if (conn != null)
            conn.disconnect();
    }
}

/**
 * Reads an UTF-8 encoded string from the specified stream.
 *
 * @param is
 * @param totalLengthInBytes
 * @return
 * @throws IOException
 */
private String readStringFromStream(InputStream is, int totalLengthInBytes) throws IOException {
    // Return empty string if the requested number of bytes is zero.
    if (totalLengthInBytes == 0)
        return "";

    // It seems that Opera 10 may pass -1 as the total length if the actual Content-Length header
    // indicates zero body length.
    // Because -1 indicates unspecified content length we attempt to read as much as possible in this case.
    if (totalLengthInBytes == -1)
        totalLengthInBytes = Integer.MAX_VALUE;

    // Read the data from the stream as bytes and pipe it through piped stream
    // that converts the byte stream to UTF-8 char stream.
    PipedOutputStream poutput = null;
    PipedInputStream pinput = null;
    StringBuilder result = new StringBuilder();
    try {
        // Start reading the stream.
        boolean continueRead = true;
        poutput = new PipedOutputStream();
        pinput = new PipedInputStream(poutput);
        InputStreamReader r = new InputStreamReader(pinput, "UTF-8");
        int bytesReadTotal = 0;
        int byteBufferSize = 500;  // Buffer size used in the conversion.
        CharBuffer cb = CharBuffer.allocate(byteBufferSize);
        byte[] buffer = new byte[byteBufferSize];
        while (continueRead) {
            // Read correct number of bytes from the input stream and write the to the output buffer.
            int readByteCount = Math.min(buffer.length, totalLengthInBytes - bytesReadTotal);
            int bytesRead = is.read(buffer, 0, readByteCount);

            // Convert the bytes to a string.
            if (bytesRead > 0) {
                // Write to the piped stream.
                poutput.write(buffer, 0, bytesRead);

                // Read the bytes as string.
                cb.clear();
                r.read(cb);
                int charactersRead = cb.position();

                // Collect the string read to the buffer.
                cb.rewind();
                String currentBatch = cb.subSequence(0, charactersRead).toString();
                result.append(currentBatch);

            }  // end if

            // Stop reading if EOF was encountered.
            if (bytesRead == -1)
                continueRead = false;
            else
                bytesReadTotal += bytesRead;

            // Stop reading the stream after we have read all the available bytes.
            if (bytesReadTotal == totalLengthInBytes)
                continueRead = false;

        }  // end while
    } finally {
        // Close the middleware streams.
        closeStream(poutput);
        closeStream(pinput);
    }

    // Return the result.
    return result.toString();
}

/**
 * Closes the specified stream
 *
 * @param stream
 */
private static void closeStream(Closeable stream) {
    // Try closing only if the stream was specified.
    if (stream != null) {
        try {
            stream.close();
        } catch (IOException e) {
            // Ignore error.
            e.printStackTrace();
        }
    }
 }
}

我用来下载文件的行是:

try {
FileOutputStream outputStream = new FileOutputStream(file);
outputStream = WSClass.doMethod(Url, WSClass.HTTPMethod.GET, null, 
FileOutputStream.class);
 } catch (FileNotFoundException e) {
      e.printStackTrace();
  } catch (SocketException e) {
    e.printStackTrace();
   } catch (IOException e) {
    e.printStackTrace();
   } catch (Exception e) {
   e.printStackTrace();
  }finally {
    if (outputStream != null) {
      outputStream.flush();
       outputStream.close();
      }
   }

有人知道这是什么问题吗?

更新:我成功下载了该文件。

我使用@Ganesh Karewad建议更改函数doMethod,并添加了另一种写入fileoutputstream的方法。

    public <T> T doMethod(String strUrl, HTTPMethod method, Object body,
                      Class<T> output, File file) throws IOException {
    return doMethod(strUrl, method, body, output, null, file);
}

/**
 * Calls a REST endpoint in the specified URL and returns
 * the return value. Optionally deserializes it from JSON.
 *
 * @param <T>      The type of the return value
 * @param //strUrl URL relative to the applet root
 * @param method   HTTP Method used in the request
 * @param body     Body serialized for the JSON
 * @param output   The type of the return value
 * @param headers  Key-Value list of additional headers.
 * @return The REST response as an instance of type T
 */
@SuppressWarnings("unchecked")
public <T> T doMethod(String strUrl, HTTPMethod method, Object body,
                      Class<T> output, Map<String, String> headers, File file) throws IOException {

    // Strip the first '/' away if it exists.
    if (strUrl.startsWith("/")) {
        strUrl = strUrl.substring(1);
    }

    // Calculate the real url based on method. IIS supports only the
    // GET and POST in default mode so we'll use the _method parameter
    // that MFWS understands.
    if (method != HTTPMethod.GET && method != HTTPMethod.POST) {
        String methodParam;
        if (strUrl.contains("?")) {
            methodParam = "&_method=";
        } else {
            methodParam = "?_method=";
        }
        strUrl += methodParam + method.name();
        method = HTTPMethod.POST;
    }

    // Initialize JSON (de)serializer.
    ObjectMapper om = new ObjectMapper();
    om.configure(org.codehaus.jackson.map.SerializationConfig.Feature.CAN_OVERRIDE_ACCESS_MODIFIERS, false);
    om.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.CAN_OVERRIDE_ACCESS_MODIFIERS, false);
    om.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    // Get URL to REST interface.
    URL u = new URL(strUrl);


    // Perform the request.
    HttpURLConnection conn = null;
    OutputStream os = null;
    InputStream is = null;
    BufferedReader in = null;
    try {

        // Open connection.
        conn = (HttpURLConnection) u.openConnection();

        // Prevent the use of cache.
        // The applet does not seem to respect the cache control flags it receives from the server.
        // For example it won't necessarily make a new request to the server even if the requested
        // resources has expired. See issue: #9234.
        conn.setUseCaches(false);

        // Set the request properties.
        conn.setRequestMethod(method.name());
        if (body != null)
            conn.setDoOutput(true);
        if (!output.equals(void.class))
            conn.setDoInput(true);
        if(file != null)
        {
            conn.setRequestProperty("Accept", "application/octet-stream");
        }
        else
        {
            conn.setRequestProperty("Accept", "application/json");
        }
        conn.setRequestProperty("X-Authentication", authenticationToken);

        if (headers != null) {
            for (Map.Entry<String, String> header : headers.entrySet()) {
                System.out.println("Setting header " + header.getKey());
                conn.setRequestProperty(header.getKey(), header.getValue());
            }
        }

        // If there is a body, serialize it to the output stream.
        if (body != null) {

            os = conn.getOutputStream();
            om.writeValue(os, body);

        } else if (method != HTTPMethod.GET) {

            // No body available.
            conn.setRequestProperty("Content-Length", "0");
        }

        // Check if the caller wanted the connection as the return value.
        if (output.equals(HttpURLConnection.class)) {
            // Change ownership so we don't disconnect the connection in
            // finalize block.
            HttpURLConnection connDetached = conn;
            conn = null;
            return (T) connDetached;
        }

        // Write the output if we had output.
        if (os != null) {
            os.flush();
            os.close();
        }
        os = null;

        // Get response to input stream.
        conn.connect();
        is = conn.getInputStream();
        String response = null;
        int contentLength = conn.getContentLength();
        if (output.equals(InputStream.class)) {
            // If the output type is input stream, just return it
            // as it is.
            InputStream isReturn = is;
            is = null;  // Change ownership.
            return (T) isReturn;
        }
        else {
                if(file != null)
                {
                    FileOutputStream fileOut = new FileOutputStream(file);
                    IOUtils.copy(is, fileOut);
                    return om.readValue("true", output);
                }
                else
                {
                    // Deserialize from JSON object.
                    response = readStringFromStream(is, contentLength);
                }


            // Read the return value from the response.
            if (output.equals(void.class) || response.length() == 0)
                return null;
            else
                return om.readValue(response, output);

        }  // end-if (output.equals(InputStream.class))

    } catch (IOException e3) {
        throw new RuntimeException(e3);
    } finally {
        // Close streams.
        closeStream(os);
        closeStream(is);
        closeStream(in);

        if (conn != null)
            conn.disconnect();
    }
}

如果我需要文件: 我将连接设置为“conn.setRequestProperty(”Accept“,”application / octet-stream“);”并读取fileoutputstream的输入。

谢谢,

塔尔

1 个答案:

答案 0 :(得分:1)

您正试图通过将值设置为

将文件下载为json
conn.setRequestProperty("Accept", "application/json");

所以java试图将响应作为json读取,这就是为什么它的抛出错误  现在,如果你不知道你将得到什么类型的文件作为回应使用

conn.setRequestProperty("Accept", "application/octet-stream");

如果您知道文件类似于png文件,请使用

conn.setRequestProperty("Accept", "image/png");