Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected character ('%' (code 37)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')

For several day I trying to figure out what this error means. I getting the error when i try to download file from web service.

The full error is:

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]".

The class that do the job of Serialization and Deserialization from web service:

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();
        }
    }
 }
}

The line the I used to download the file is:

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();
      }
   }

Someone knows what the problem?

UPDATE: I success to download the file.

I change the function doMethod with @Ganesh Karewad suggested and added another way to write to 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();
    }
}

If i need file: I set to connection as "conn.setRequestProperty("Accept", "application/octet-stream");" and read the input to fileoutputstream.

Thanks,

Tal

like image 692
tal Avatar asked Nov 08 '22 16:11

tal


1 Answers

you are trying to download file as a json by setting value as

conn.setRequestProperty("Accept", "application/json");

so java is trying to read response as a json and thats why its throwing error now if you dont' know what type of file you will get in response use

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

if you know file like its a png file then use

conn.setRequestProperty("Accept", "image/png");
like image 163
Ganesh Karewad Avatar answered Nov 14 '22 22:11

Ganesh Karewad