maneira mais simples de ler json de uma URL em java

245

Essa pode ser uma pergunta idiota, mas qual é a maneira mais simples de ler e analisar JSON a partir da URL em Java ?

No Groovy, é uma questão de poucas linhas de código. Os exemplos de Java que eu acho ridiculamente longos (e possuem um enorme bloco de manipulação de exceções).

Tudo o que eu quero fazer é ler o conteúdo deste link .

Pomponius
fonte
9
O tratamento de exceções é necessário, pois o java o força a lidar com as exceções declaradas. O que há de errado com o tratamento de exceções?
Falmarri
3
bem, o "Java forçá-lo" é o maior problema
Jonatan Cloutier
9
Se o java não o forçasse a lidar com exceções, você acha que os programas ainda rodariam e rodariam bem? E se me pedissem para inserir minha idade em um programa e eu desse o snarfleblagger como entrada? O java deve permitir que o programa seja executado sem problemas? Se você não deseja lidar com exceções, declare-as como lançadas pelos métodos em que elas podem ocorrer e observe o seu programa falhar quando algo não estiver perfeitamente correto.
Richard Barker
2
Não é uma pergunta idiota. Especialmente vindo do PHP, onde você pode fazer isso json_decode(file_get_contents($url));e pronto!
dtbarne

Respostas:

193

Usando o artefato Maven org.json:json, obtive o código a seguir, que acho bastante curto. Não o mais curto possível, mas ainda utilizável.

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
fonte
3
em vez de ler caractere por caractere, você pode usar readLine () no BufferedReader. Isso reduzirá o número de iterações do loop while.
Kdabir
6
Pelo que? A readLinefunção fará o loop então, e eu tenho que concatenar as linhas em vez dos caracteres, o que é mais caro. Isso não manteria o código tão curto quanto agora. Além disso, na notação JSON não há conceito de "linhas", então por que devo lê-las como tal?
Roland Illig 01/12/10
4
Considere o IOUtils.toString(InputStream)método do Apache commons-io . Isso deve economizar algumas linhas e responsabilidades.
Mark Tielemans
3
Por que estou recebendo esse erro "O construtor JSONObject (String) está indefinido" na linha de JSONObject json = new JSONObject (jsonText); no método "readJsonFromUrl" ..? @RolandIllig
pop
2
Com o GSON, Jackson, Boon, Genson e outros, você só precisa alimentar a fonte: apenas URL ou, no máximo InputStream. Embora o código acima possa ser curto para org.jsonuso, verifique outras bibliotecas disponíveis - não há necessidade de escrever mais de 1 a 3 linhas de código para esta tarefa.
StaxMan
68

Aqui estão algumas versões alternativas de Jackson (já que existem mais de uma maneira de você querer dados como):

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

E, especificamente, o caso usual (IMO) em que você deseja lidar com objetos Java, pode ser criado de uma só vez:

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

Outras bibliotecas como o Gson também suportam métodos de uma linha; por que muitos exemplos mostram seções muito mais longas é estranho. E pior ainda: muitos exemplos usam a biblioteca org.json obsoleta; pode ter sido a primeira coisa a surgir, mas existem meia dúzia de alternativas melhores, portanto, há muito pouco motivo para usá-lo.

StaxMan
fonte
O que é url aqui? É um objeto de string ou URL ou byte []?
Dinesh
1
Eu estava pensando em java.net.URLmas de qualquer um funciona, bem como a abundância de outras fontes ( File, InputStream, Reader, String).
precisa saber é o seguinte
4
Ele deve ser java.net.URL no exemplo acima, caso contrário, ele tentará analisar a string 'http: // ....' como um json, o que gerará um erro
zbstof 30/12/15
@Zotov Sim. A passagem Stringexigiria que o conteúdo fosse JSON, e não a codificação textual do URI / URL para uso.
StaxMan 4/16/16
2
O token não reconhecido 'https': estava esperando ('verdadeiro', 'falso' ou 'nulo')
Damir Olejar
60

A maneira mais fácil: use gson, a própria biblioteca goto json do google. https://code.google.com/p/google-gson/

Aqui está uma amostra. Eu estou indo para este site de geolocalização gratuito e analisando o json e exibindo meu CEP. (basta colocar esse material no método principal para testá-lo)

    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
user2654569
fonte
1
Por que estou recebendo o erro 'NetworkOnMainThreadException'? Eu devo usar um AsyncTask ou existe outra maneira? Neste exemplo, você recebeu esse erro também?
Benetz
Typo, deve ser:rootobj.get("zip_code").getAsString();
loopasam
Como importo o jsonParser? Sempre recebo o erro: 'Erro: (69, 9) erro: não é possível encontrar a classe de símbolo JsonParser'
MAESTRO_DE 11/11/16
1
Adicione dependência maven <dependency> <groupId> com.google.code.gson </groupId> <artifactId> gson </artifactId> <version> 2.8.0 </version> </dependency> para obter o JsonParse ao seu alcance Arquivo.
sashikanta
pode ser simplificado e manipulado de maneira segura com o Gson, como: MyResponseObject response = new GsonBuilder (). create (). fromJson (new InputStreamReader ((InputStream) request.getContent ()), MyResponseObject.class);
22719 Gregor
42

Se você não se importa em usar algumas bibliotecas, isso pode ser feito em uma única linha.

Inclua Apache Commons IOUtils e bibliotecas json.org .

JSONObject json = new JSONObject(IOUtils.toString(new URL("https://graph.facebook.com/me"), Charset.forName("UTF-8")));
ezwrighter
fonte
Você tem alguma idéia de por que adicionar a dependência ao gradle impediria a instalação deste aplicativo no meu Google Pixel XL?
andrdoiddev
@andrdoiddev - Você deve fazer isso como uma pergunta separada. É mais geral para o Apache Commons, Gradle e Android Development.
ezwrighter
1
Isso é bastante antigo agora, mas ainda é uma boa solução, portanto, caso o @andrdoiddev ou qualquer outra pessoa ainda precise das dependências Gradle, estas são as que eu estou usando: compile group: 'org.json', nome: 'json ', versão:' 20180813 'grupo de compilação:' commons-io ', nome:' commons-io ', versão:' 2.6 '
r02 12/12/18
Digamos que você esteja disparando várias solicitações, salvando as respostas json em um JSONArray e depois gravando o JSONArray em um arquivo usando o FileWriter, então esta biblioteca NÃO ESCAPA aspas duplas. Isso faz com que o arquivo seja analisado facilmente de volta ao JSONArray.
Gh0sT
6

Use HttpClient para pegar o conteúdo da URL. E, em seguida, use a biblioteca do json.org para analisar o JSON. Eu usei essas duas bibliotecas em muitos projetos e elas foram robustas e simples de usar.

Fora isso, você pode tentar usar uma biblioteca java da API do Facebook. Não tenho nenhuma experiência nessa área, mas há uma pergunta sobre o estouro de pilha relacionada ao uso de uma API do Facebook em java . Você pode considerar o RestFB como uma boa opção para uma biblioteca usar.

Jon Snyder
fonte
5

Eu fiz o analisador de json da maneira mais simples, aqui 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;

}
}

essa classe retorna o objeto json da url

e quando você deseja o objeto json, basta chamar essa classe e o método na sua classe Activity

meu código está aqui

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

aqui a "chave json" é denotada como a chave no seu arquivo json

este é um exemplo simples de arquivo json

{
    "json":"hi"
}

Aqui "json" é a chave e "oi" é o valor

Isso fará com que seu valor json corresponda ao conteúdo da string.

Ribin Das
fonte
1
Eu acho que não é a "maneira mais simples"
fique sozinho
3

É muito fácil, usando jersey-client, apenas inclua esta dependência:

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

Em seguida, chame-o usando este exemplo:

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

Em seguida, use o Gson do Google para analisar o JSON:

Gson gson = new Gson();
Type gm = new TypeToken<CoinDeskMessage>() {}.getType();
CoinDeskMessage cdm = gson.fromJson(json, gm);
user3892260
fonte
Nada contra a resposta, mas muitos problemas com o pacote jersey-client ... é uma bagunça de erros.
Johnny Bigoode
Não encontrei nenhum problema com ele. você pode apontar algumas especificidades e / ou alternativas?
user3892260
Os problemas encontrados foram relacionados ao erro não encontrado na classe, não tinham exatamente certeza da causa, porque não consegui corrigi-lo.
Johnny Bigoode
3

Eu descobri que essa é a maneira mais fácil de longe.

Use 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;
    }

E use-o assim:

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
fonte
Você tem uma exceção ClassCastException ali. Alterar o URL da linha u = novo URL (url); para este URL u = novo URL (null, url, new sun.net.www.protocol.https.Handler ()); para que este código funcione
sunny arya
2

Não tenho certeza se isso é eficiente, mas esta é uma das maneiras possíveis:

Leia json do uso de URL url.openStream()e leia o conteúdo em uma string.

construa um objeto JSON com esta string (mais em json.org )

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

Eu queria adicionar uma resposta atualizada aqui, já que (um pouco) as atualizações recentes no JDK tornaram um pouco mais fácil a leitura do conteúdo de uma URL HTTP. Como outros já disseram, você ainda precisará usar uma biblioteca JSON para fazer a análise, pois o JDK atualmente não contém uma. Aqui estão algumas das bibliotecas JSON mais usadas para Java:

Para recuperar o JSON de uma URL, esta parece ser a maneira mais simples de usar estritamente classes JDK (mas provavelmente não é algo que você gostaria de fazer para cargas úteis grandes), o Java 9 introduziu: 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 analisar o JSON usando a biblioteca GSON, por exemplo

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

Aqui está uma amostra completa de como analisar o conteúdo Json. O exemplo usa as estatísticas de versões do Android (encontradas no código-fonte do Android Studio aqui , com links para aqui ).

Copie o arquivo "distributions.json" obtido de lá para res / raw, como substituto.

build.gradle

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

manifesto

  <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 à dependência, você também pode usar isso:

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

e o fallback:

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

O resultado de executar isso:

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%
desenvolvedor android
fonte