La forma más sencilla de leer json desde una URL en Java

245

Esta podría ser una pregunta tonta, pero ¿cuál es la forma más sencilla de leer y analizar JSON desde URL en Java ?

En Groovy, se trata de pocas líneas de código. Los ejemplos de Java que encuentro son ridículamente largos (y tienen un gran bloque de manejo de excepciones).

Todo lo que quiero hacer es leer el contenido de este enlace .

Pomponio
fuente
99
El manejo de excepciones es necesario ya que Java lo obliga a manejar cualquier excepción que se declare. ¿Qué hay de malo en el manejo de excepciones?
Falmarri
3
bueno, "java force you" es el mayor problema
Jonatan Cloutier
99
Si Java no te obligara a manejar excepciones, ¿crees que los programas seguirían funcionando bien? ¿Qué pasa si me pidieron que ingrese mi edad en un programa y le di a snarfleblagger como mi aporte? ¿Java debería permitir que el programa se ejecute sin problemas? Si no desea manejar las excepciones, declare que son arrojadas por los métodos en los que pueden ocurrir y observe cómo su programa falla cuando algo no está perfectamente bien.
Richard Barker
2
No es una pregunta tonta en absoluto. Especialmente viniendo de PHP, donde puedes hacer esto json_decode(file_get_contents($url));y ¡listo!
dtbarne

Respuestas:

193

Usando el artefacto Maven org.json:jsonobtuve el siguiente código, que creo que es bastante corto. No lo más corto posible, pero aún utilizable.

package so4308554;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.nio.charset.Charset;

import org.json.JSONException;
import org.json.JSONObject;

public class JsonReader {

  private static String readAll(Reader rd) throws IOException {
    StringBuilder sb = new StringBuilder();
    int cp;
    while ((cp = rd.read()) != -1) {
      sb.append((char) cp);
    }
    return sb.toString();
  }

  public static JSONObject readJsonFromUrl(String url) throws IOException, JSONException {
    InputStream is = new URL(url).openStream();
    try {
      BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
      String jsonText = readAll(rd);
      JSONObject json = new JSONObject(jsonText);
      return json;
    } finally {
      is.close();
    }
  }

  public static void main(String[] args) throws IOException, JSONException {
    JSONObject json = readJsonFromUrl("https://graph.facebook.com/19292868552");
    System.out.println(json.toString());
    System.out.println(json.get("id"));
  }
}
Roland Illig
fuente
3
en lugar de leer carácter por carácter, puede usar readLine () en BufferedReader. Esto reducirá el número de iteraciones del ciclo while.
kdabir
66
¿Para qué? La readLinefunción hará el ciclo entonces, y tengo que concatenar las líneas en lugar de los caracteres, lo cual es más costoso. Eso no mantendría el código tan corto como lo es ahora. Además, en la notación JSON no existe el concepto de "líneas", entonces, ¿por qué debería leerlas como tales?
Roland Illig
44
Considere el IOUtils.toString(InputStream)método de Apache commons-io . Eso debería ahorrarle algunas líneas y responsabilidad.
Mark Tielemans
3
Por qué recibo este error "El constructor JSONObject (String) no está definido" en la línea de JSONObject json = new JSONObject (jsonText); en el método "readJsonFromUrl" ..? @RolandIllig
Karthick pop
2
Con GSON, Jackson, Boon, Genson y otros, solo necesita alimentar la fuente: ya sea solo URL, o como máximo InputStream. Si bien el código anterior puede ser corto para su org.jsonuso, asegúrese de verificar otras bibliotecas disponibles; no es necesario escribir más de 1-3 líneas de código para esta tarea.
StaxMan
68

Aquí hay un par de versiones alternativas con Jackson (ya que hay más de una forma en que puede desear datos):

  ObjectMapper mapper = new ObjectMapper(); // just need one
  // Got a Java class that data maps to nicely? If so:
  FacebookGraph graph = mapper.readValue(url, FaceBookGraph.class);
  // Or: if no class (and don't need one), just map to Map.class:
  Map<String,Object> map = mapper.readValue(url, Map.class);

Y, específicamente, el caso habitual (IMO) en el que desea tratar con objetos Java, puede hacerse un revestimiento:

FacebookGraph graph = new ObjectMapper().readValue(url, FaceBookGraph.class);

Otras bibliotecas como Gson también admiten métodos de una línea; Por qué muchos ejemplos muestran secciones mucho más largas es extraño. Y aún peor es que muchos ejemplos usan la biblioteca obsoleta org.json; Puede haber sido lo primero, pero hay media docena de alternativas mejores, por lo que hay muy pocas razones para usarlo.

StaxMan
fuente
¿Qué es url aquí? ¿Es una cadena o un objeto URL o un byte []?
Dinesh
1
Estaba pensando java.net.URL, pero cualquiera de los dos trabajos, así como un montón de otras fuentes ( File, InputStream, Reader, String).
StaxMan
44
Se debe ser java.net.URL en el ejemplo anterior, si no se tratará de cadena de análisis sintáctico 'http: // ....' como JSON, que producirá un error
zbstof
@Zotov Sí. Pasar Stringrequeriría que los contenidos sean JSON, y no una codificación textual de URI / URL para usar.
StaxMan
2
Token no reconocido 'https': estaba esperando ('verdadero', 'falso' o 'nulo')
Damir Olejar el
60

La forma más fácil: use gson, la propia biblioteca goto json de google. https://code.google.com/p/google-gson/

Aquí hay una muestra. Voy a este sitio web de geolocalizador gratuito y analizo el json y visualizo mi código postal. (solo pon estas cosas en un método principal para probarlo)

    String sURL = "http://freegeoip.net/json/"; //just a string

    // Connect to the URL using java's native library
    URL url = new URL(sURL);
    URLConnection request = url.openConnection();
    request.connect();

    // Convert to a JSON object to print data
    JsonParser jp = new JsonParser(); //from gson
    JsonElement root = jp.parse(new InputStreamReader((InputStream) request.getContent())); //Convert the input stream to a json element
    JsonObject rootobj = root.getAsJsonObject(); //May be an array, may be an object. 
    String zipcode = rootobj.get("zip_code").getAsString(); //just grab the zipcode
usuario2654569
fuente
1
¿Por qué recibo el error 'NetworkOnMainThreadException'? ¿Debo usar una AsyncTask o hay otra forma? En este ejemplo, ¿también recibió este error?
Benetz
Typo, debe ser:rootobj.get("zip_code").getAsString();
loopasam
¿Cómo importo el jsonParser? Siempre recibo el error: 'Error: (69, 9) error: no puedo encontrar la clase de símbolo JsonParser'
MAESTRO_DE
1
Agregue la dependencia de Maven <dependency> <groupId> com.google.code.gson </groupId> <artifactId> gson </artifactId> <version> 2.8.0 </version> </dependency> para obtener el JsonParse en su pom expediente.
sashikanta
se puede simplificar y manejar de forma segura con Gson como: MyResponseObject response = new GsonBuilder (). create (). fromJson (new InputStreamReader ((InputStream) request.getContent ()), MyResponseObject.class);
Gregor
42

Si no le importa usar un par de bibliotecas, puede hacerlo en una sola línea.

Incluya las bibliotecas Apache Commons IOUtils y json.org .

JSONObject json = new JSONObject(IOUtils.toString(new URL("https://graph.facebook.com/me"), Charset.forName("UTF-8")));
ezwrighter
fuente
¿Tienes alguna idea de por qué agregar la dependencia al gradle evitaría que esta aplicación se instale en mi Google Pixel XL?
andrdoiddev
@andrdoiddev: debe hacer eso como una pregunta separada. Es más general para Apache Commons, Gradle y Android Development.
ezwrighter
1
Esto es bastante antiguo ahora, pero sigue siendo una buena solución, así que en caso de que @andrdoiddev o alguien más necesite las dependencias de Gradle, estas son las que estoy usando: grupo de compilación: 'org.json', nombre: 'json ', versión:' 20180813 'grupo de compilación:' commons-io ', nombre:' commons-io ', versión:' 2.6 '
r02
Digamos que está disparando múltiples solicitudes, guardando las respuestas json en un JSONArray y luego escribiendo el JSONArray en un archivo usando FileWriter, luego esta biblioteca NO ESCAPA comillas dobles. Esto hace que el archivo se vuelva a analizar fácilmente en JSONArray.
Gh0sT
6

Use HttpClient para obtener el contenido de la URL. Y luego use la biblioteca de json.org para analizar el JSON. He usado estas dos bibliotecas en muchos proyectos y han sido robustas y fáciles de usar.

Aparte de eso, puede intentar usar una biblioteca Java API de Facebook. No tengo ninguna experiencia en esta área, pero hay una pregunta sobre el desbordamiento de pila relacionado con el uso de una API de Facebook en Java . Es posible que desee ver RestFB como una buena opción para una biblioteca.

Jon Snyder
fuente
5

He hecho el analizador json de la manera más simple, aquí está

package com.inzane.shoapp.activity;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONException;
import org.json.JSONObject;

import android.util.Log;

public class JSONParser {

static InputStream is = null;
static JSONObject jObj = null;
static String json = "";

// constructor
public JSONParser() {

}

public JSONObject getJSONFromUrl(String url) {

    // Making HTTP request
    try {
        // defaultHttpClient
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpPost httpPost = new HttpPost(url);

        HttpResponse httpResponse = httpClient.execute(httpPost);
        HttpEntity httpEntity = httpResponse.getEntity();
        is = httpEntity.getContent();

    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    } catch (ClientProtocolException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

    try {
        BufferedReader reader = new BufferedReader(new InputStreamReader(
                is, "iso-8859-1"), 8);
        StringBuilder sb = new StringBuilder();
        String line = null;
        while ((line = reader.readLine()) != null) {
            sb.append(line + "\n");
            System.out.println(line);
        }
        is.close();
        json = sb.toString();

    } catch (Exception e) {
        Log.e("Buffer Error", "Error converting result " + e.toString());
    }

    // try parse the string to a JSON object
    try {
        jObj = new JSONObject(json);
    } catch (JSONException e) {
        Log.e("JSON Parser", "Error parsing data " + e.toString());
        System.out.println("error on parse data in jsonparser.java");
    }

    // return JSON String
    return jObj;

}
}

esta clase devuelve el objeto json de la url

y cuando quieres el objeto json simplemente llamas a esta clase y al método en tu clase Activity

mi codigo esta aqui

String url = "your url";
JSONParser jsonParser = new JSONParser();
JSONObject object = jsonParser.getJSONFromUrl(url);
String content=object.getString("json key");

aquí la "clave json" se denota como la clave en su archivo json

este es un ejemplo simple de archivo json

{
    "json":"hi"
}

Aquí "json" es clave y "hola" es valor

Esto obtendrá su valor json para encadenar contenido.

Ribin Das
fuente
1
Creo que no es la "forma más sencilla"
solo el
3

Es muy fácil, usando jersey-client, solo incluye esta dependencia de Maven:

<dependency>
  <groupId>org.glassfish.jersey.core</groupId>
  <artifactId>jersey-client</artifactId>
  <version>2.25.1</version>
</dependency>

Luego invoque usando este ejemplo:

String json = ClientBuilder.newClient().target("http://api.coindesk.com/v1/bpi/currentprice.json").request().accept(MediaType.APPLICATION_JSON).get(String.class);

Luego use Gson de Google para analizar el JSON:

Gson gson = new Gson();
Type gm = new TypeToken<CoinDeskMessage>() {}.getType();
CoinDeskMessage cdm = gson.fromJson(json, gm);
usuario3892260
fuente
Nada en contra de la respuesta, pero tantos problemas con el paquete jersey-cliente ... es un desastre de errores.
Johnny Bigoode
No he encontrado ningún problema con él. ¿Puedes señalar algunos detalles y / o alternativas?
user3892260
Los problemas que encontré estaban relacionados con un error no encontrado en la clase, no estaba exactamente seguro de la causa, porque no pude solucionarlo.
Johnny Bigoode
3

He encontrado que esta es la forma más fácil con diferencia.

Usa este método:

public static String getJSON(String url) {
        HttpsURLConnection con = null;
        try {
            URL u = new URL(url);
            con = (HttpsURLConnection) u.openConnection();

            con.connect();


            BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line + "\n");
            }
            br.close();
            return sb.toString();


        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            if (con != null) {
                try {
                    con.disconnect();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
        return null;
    }

Y úsalo así:

String json = getJSON(url);
JSONObject obj;
   try {
         obj = new JSONObject(json);
         JSONArray results_arr = obj.getJSONArray("results");
         final int n = results_arr.length();
            for (int i = 0; i < n; ++i) {
                // get the place id of each object in JSON (Google Search API)
                String place_id = results_arr.getJSONObject(i).getString("place_id");
            }


   }
mastercool
fuente
Tienes una excepción ClassCastException sentada allí. Cambiar la línea URL u = nueva URL (url); a esta URL u = nueva URL (nulo, url, nuevo sun.net.www.protocol.https.Handler ()); para que este código funcione
arya soleado
2

No estoy seguro de si esto es eficiente, pero esta es una de las formas posibles:

Lea json del uso de url url.openStream()y lea el contenido en una cadena.

construir un objeto JSON con esta cadena (más en json.org )

JSONObject(java.lang.String source)
      Construct a JSONObject from a source JSON text string.
kdabir
fuente
0

Quería agregar una respuesta actualizada aquí ya que (algo) las actualizaciones recientes al JDK han hecho que sea un poco más fácil leer el contenido de una URL HTTP. Como otros han dicho, aún necesitará usar una biblioteca JSON para realizar el análisis, ya que el JDK actualmente no contiene una. Estas son algunas de las bibliotecas JSON más utilizadas para Java:

Para recuperar JSON de una URL, esta parece ser la forma más sencilla de usar estrictamente clases JDK (pero probablemente no es algo que quiera hacer para grandes cargas), Java 9 introdujo: https://docs.oracle.com/en/ java / javase / 11 / docs / api / java.base / java / io / InputStream.html # readAllBytes ()

try(java.io.InputStream is = new java.net.URL("https://graph.facebook.com/me").openStream()) {
    String contents = new String(is.readAllBytes());
}

Para analizar el JSON usando la biblioteca GSON, por ejemplo

com.google.gson.JsonElement element = com.google.gson.JsonParser.parseString(contents); //from 'com.google.code.gson:gson:2.8.6'
Scott
fuente
0

Aquí hay una muestra completa de cómo analizar el contenido de Json. El ejemplo toma las estadísticas de las versiones de Android (que se encuentra a partir del código fuente de Android Studio aquí , que une a aquí ).

Copie el archivo "distributions.json" que obtiene desde allí en res / raw, como respaldo.

build.gradle

    implementation 'com.google.code.gson:gson:2.8.6'

manifiesto

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

MainActivity.kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        if (savedInstanceState != null)
            return
        thread {
            // https://cs.android.com/android/platform/superproject/+/studio-master-dev:tools/adt/idea/android/src/com/android/tools/idea/stats/DistributionService.java
            var root: JsonArray
            Log.d("AppLog", "loading...")
            try {
                HttpURLConnection.setFollowRedirects(true)
                val statsUrl = "https://dl.google.com/android/studio/metadata/distributions.json" //just a string
                val url = URL(statsUrl)
                val request: HttpURLConnection = url.openConnection() as HttpURLConnection
                request.connectTimeout = 3000
                request.connect()
                InputStreamReader(request.content as InputStream).use {
                    root = JsonParser.parseReader(it).asJsonArray
                }
            } catch (e: Exception) {
                Log.d("AppLog", "error while loading from Internet, so using fallback")
                e.printStackTrace()
                InputStreamReader(resources.openRawResource(R.raw.distributions)).use {
                    root = JsonParser.parseReader(it).asJsonArray
                }
            }
            val decimalFormat = DecimalFormat("0.00")
            Log.d("AppLog", "result:")

            root.forEach {
                val androidVersionInfo = it.asJsonObject
                val versionNickName = androidVersionInfo.get("name").asString
                val versionName = androidVersionInfo.get("version").asString
                val versionApiLevel = androidVersionInfo.get("apiLevel").asInt
                val marketSharePercentage = androidVersionInfo.get("distributionPercentage").asFloat * 100f
                Log.d("AppLog", "\"$versionNickName\" - $versionName - API$versionApiLevel - ${decimalFormat.format(marketSharePercentage)}%")
            }
        }
    }
}

Como alternativa a la dependencia, también puede usar esto en su lugar:

InputStreamReader(request.content as InputStream).use {
    val jsonArray = JSONArray(it.readText())
}

y la alternativa:

InputStreamReader(resources.openRawResource(R.raw.distributions)).use {
    val jsonArray = JSONArray(it.readText())
}

El resultado de ejecutar esto:

loading...
result:
"Ice Cream Sandwich" - 4.0 - API15 - 0.20%
"Jelly Bean" - 4.1 - API16 - 0.60%
"Jelly Bean" - 4.2 - API17 - 0.80%
"Jelly Bean" - 4.3 - API18 - 0.30%
"KitKat" - 4.4 - API19 - 4.00%
"Lollipop" - 5.0 - API21 - 1.80%
"Lollipop" - 5.1 - API22 - 7.40%
"Marshmallow" - 6.0 - API23 - 11.20%
"Nougat" - 7.0 - API24 - 7.50%
"Nougat" - 7.1 - API25 - 5.40%
"Oreo" - 8.0 - API26 - 7.30%
"Oreo" - 8.1 - API27 - 14.00%
"Pie" - 9.0 - API28 - 31.30%
"Android 10" - 10.0 - API29 - 8.20%
desarrollador de Android
fuente