마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

retrofit2를 이용해 원하는 데이터만 추출하고 싶습니다.

0 추천

다음과같이 JSON 형식의 유사API 에서

 

Pricechart의 첫번째 Price값만 추출하여 사용하고 싶습니다.

 

http://152.70.248.4:5000/trade/{itemId}

해당 API의 주소형식이고

 

public interface RetrofitInterface {
    @GET("trade/{post}")
    Call<DataClass> getPrice(@Path("post") String post);
}
Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://152.70.248.4:5000/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();
Call<DataClass> call = service.getPrice(6885708);

call.enqueue(new Callback<DataClass>() {
    @Override
    public void onResponse(Call<DataClass> call, Response<DataClass> response) {
        if (response.isSuccessful()) {
            DataClass result = response.body();
            Log.d(TAG, "onResponse: 성공"+result.toString());
        }else{
            Log.d(TAG,"onResponse:실패");
        }
    }

onResponse: 성공PriceResult{Name=오레하 유물,Pricechart =[Lcom.example.retrofittest.PricechartData;@fb9e53b,Result=Success}

 

실행시키면 이런식으로 뜹니다.

public class DataClass {

    @SerializedName("Name")
    public String name;

    @SerializedName("Pricechart")
    public PricechartData[] pricechart;

    @SerializedName("Result")
    public String result;

    @Override
    public String toString(){
        return "PriceResult{"+
                "Name="+name+
                ",Pricechart ="+pricechart+
                ",Result="+result+'}';
    }
}
public class PricechartData {
    @SerializedName("Amount")
    public String amount;

    @SerializedName("Price")
    public String price;

    @Override
    public String toString(){
        return "PriceChart{"+
                "Amount="+amount+
                ",Price="+price+'}';
    }
}

 

DTO는 amount값과 price값에 쉼표가 들어가있어서 String으로 지정해뒀는데

 

어떻게해야 주소값을 넣으면 첫번째 Price값만 추출하여 사용할 수 있을까요..

제 짧은 지식으로는 아무리 머리를 굴려도 해답이 안나와서 도움을 청합니다.

 

어떤부분이 해결에 필요한지 몰라 답변으로 요청해주시면 감사하겠습니다

 

 

도움이 필요한 부분)

API에서 첫번째 price값을 item1= price1 형식으로 가져오는 방법

aka (120 포인트) 님이 2022년 5월 16일 질문

1개의 답변

0 추천

DataClass(좀 더 가독성있는 이름을 사용하시면 좋을 것 같네요)에 있는 PriceChartData[]는 List<PriceChartData>타입으로 바꿔서 사용하세요. 자바에서는 Array보다는 가급적이면 List타입이 컨트롤이 더 쉽습니다.

먼저 서버에서 가져온 데이터를 화면에 필요한 데이터타입으로 변환할 데이터타입을 하나 준비하세요.

public class UiChartData {
    public final String name;
    public final  List<UiChartPrice> prices;
    public final  String result;

    public UiChartData(String name, List<UiChartPrice> prices,  String result) {
         this.name = name;
         this.prices = prices;
         this.result = result;
    }

    public static UiChartData from(DataClass data) {
         List<UiChartPrice> prices = new ArrayList<>();
         for (PricechartData price : data.pricechart) {
               prices.add(UiChartPrice.from(price)  );
         }
         return new UiChartData(
             data.name,
             prices,
             data.result
         );
    }
}

public class UiChartPrice {
    public final BigDecimal amount;
    public final BigDecimal price;
    
    public UiChartPrice(BigDecimal amount, BigDecimal price) {
          this.amount = amount;
          this.price = price;
    }

    
    public static UiChartPrice from(PricechartData data) {
         return UiChartPrice(
               NumberUtils.commaStringToBigDecimal(data.amount),
                NumberUtils.commaStringToBigDecimal(data.price)
         );
    }
}


public class NumberUtils {
    public static BigDecimal commaStringToBigDecimal(String s) {
          return BigDecimal(s.replaceAll(",","").trim());
   }
} 
}

 

call.enqueue(new Callback<DataClass>() {
    @Override
    public void onResponse(Call<DataClass> call, Response<DataClass> response) {
        if (response.isSuccessful()) {
            handlePriceChartData(response.body());
        }else{
            Log.d(TAG,"onResponse:실패");
        }
}


private void handlePriceChartData(DataClass charData) {
     UiChartData uiChartData = UiChartData.from(charData);
     if (!uiChartData.prices.isEmpty()) {
          doSomething(uiChartData.prices.get(0));
     }
}

 

추가설명을 드리자면 소수점 연산의 경우 Float나 Double은 정확한 결과를 리턴하지 않을 수가 있습니다. 이런 이유로 금액같은  것을 다룰 때는 BigDecimal타입을 쓰는 것이 일반적입니다.

그리고, API와 화면에 별도의 클래스를 두는 이유는 화면에서 요구되어지는 데이터와 서버에서 리턴하는 데이터 간에는 항상 차이가 생기기가 쉽습니다. 따라서 서버의 응답데이터를 조작하기 보다는 화면에 적합한 클래스를 두어서 이를 가지고 화면에서 사용하는 것이 더 적합한 접근방법입니다.

참고로, 또다른 접근방법은 Gson의   Custom descrializer을 사용해서 Data클래스를 파싱하거나 string 필드를 님이 정의한 숫자형태의 클래스로 변환할 수도 있습니다. 이렇게 하면  amount와 prce 필드를 string이 아니라 숫자타입으로 리턴할 수 있습니다.  

그리고 콤마를 호함한 String을 BigDecimal를 변환할 때 상황에 따라 BigDecimal에서 제공하는 setParseBigDecimal메소드를 사용할 수도 있습니다.

https://docs.oracle.com/javase/9/docs/api/java/text/DecimalFormat.html#setParseBigDecimal-boolean-

 

spark (226,720 포인트) 님이 2022년 5월 16일 답변
spark님이 2022년 5월 17일 수정
제가 안드로이드 쪽이 완전 초보라 몇가지 이해가 안되는 부분이 있어서 추가 질문 드립니다.

 public static UiChartData from(DataClass data) {
        List<UiChartPrice> prices = new ArrayList<>();
        for (PricechartData price : data.pricechart) {
            prices.add(price);
        }
        return UiChartData(
                data.name,
                prices,
                data.result
        );
    }
}

이부분에서 price 값이
incompatible types: PricechartData cannot be converted to UiChartPrice
라며 자료형이 맞지않다고 뜨네요.
그래서 UiChartPrice를 사용하는 다른클래스들도 오류가 납니다.

PricechartData의 경우 List<PricechartData>로 변경했는데 뭐가 문제일까요?

머리를 싸잡고 고민해봐도 DataClass의 pricechart값을 그대로 대입한건데
안되는 이유를 모르겠습니다..
prices.add(UiChartPrice.from(price)  );
루프 안이 위처럼 바뀌어야 겠네요.
감사합니다.
약간의 추가질문드려도될까요?
근데 static을 사용한 함수에서 return값이 계속 오류가 떠서 new를 넣었는데 괜찮은건가요?
그리고 doSomething이란 메소드는 임의의 메소드고
필요한 내용을 작성하여 사용하면 되나요?
네. 그래서 메소드 이름이 doSomething입니다.
2주만에 다시 질문드립니다.
doSometing(uiChartData.prices.get(0));
에서 uiChartData 자료형을 어떻게 정의해야 사용할수있는지 모르겠습니다
스트링타입으로 바꿔서 저장하면 주소형식으로 뜨던데 어떻게해야
변수에 값을 저장할수있을까요 ㅜㅜ?
Object.toString()은 오버라이드를 하면 인스턴스의 값이 출력됩니다. 따라서 Object.toString()을 사용하실 계획이라면 해당 클래스에서 override 하셔야 합니다.(Android Studio의 해당 클래스에서 오른쪽 마우스를 누르면 컨텍스트 메뉴중에 오버라이드 코드를 자동으로 완성시켜주는 기능이 있습니다.)

더 좋은 옵션은 uiChartData.prices.get(0)으로 리턴된 클래스타입 중 적절한 필드를 사용하거나 메소드를 하나 제공하는 겁니다. 님의 경우는 amount.toString() 나  price.toString()을 사용하는게 가장 간단해 보이네요. 이 경우는 아래처럼 해보세요.

public class UiChartData {
    ...
   public BigDecimal firstPrice() {
      return uiChartData.prices.get(0);
   }
}


호출시에는
doSometing(uiChartData.firstPrice().toString());

그리고 doSomething은 적절하지 않은 이름으로, 적절한 메소드명을 사용하세요. 참고로 수많은 개발자가 동의하는 좋은 코드의 첫번째 조건 중의 하나가 의도가 드러나는 적절한 이름을 사용하는 겁니다.
알려주신
public class UiChartData {
    ...
   public BigDecimal firstPrice() {
      return uiChartData.prices.get(0);
   }
}
가 적용이 안되어서 임의로


private void handlePriceChartData(DataClass charData) {
                UiChartData uiChartData = UiChartData.from(charData);
                if (!uiChartData.prices.isEmpty()) {
                   dataProcess(uiChartData.prices.get(0),rareBeef);
                   Log.d(TAG, ""+rareBeef);
                }
            }
            private void dataProcess(UiChartPrice uiChartPrice,BigDecimal bigDecimal) {
                bigDecimal = uiChartPrice.firstPrice();
                textViewResult.setText(""+bigDecimal);
            }


이런식으로 값을 대입하였는데
텍스트는 정상적으로 값이 나오는데
로그의 rareBeef는 값이 안나오네요 ㅠ

알려주신것만큼 열심히 하고싶은데 제가 너무 자바와 안드로이드 둘다 너무 초보라서 어떤식으로 응용해야 할지 감이 안잡힙니다..

그냥 rarebeef에 값만 들어가면 때려넣기 식으로라도 앱을 만들고 싶은데 어렵네요
rareBeef라는 값이 어디에서 온 건지 모르겠네요. 그리고 dataProcess함수가 좀 이상합니다. handlePriceChartData에서 이미 uiChartData.prices.get(0)는 BigDecimal타입인데, dataPricess의 인자의 타입은  UiChartPrice네요. 메소드의 선언과 호출하는쪽에서 사용하는 타입을 일치시켜주셔야 하는데요.
beefrare는 임의의 변수명입니다
그리고 댓글에 사진첨부가 안되는데

dataProcess 메소드의 받는 타입이 UiChartPrice로 나옵니다
변수인자를 BigDecimal로 받으면 오류가 납니다 ㅜㅜ
UiChartPrice 타입이 맞네요. dataProcess메소드를 보시면 인자인 bigDecimal에 다시
chartPrice를 할당하셨네요.
private void dataProcess(UiChartPrice uiChartPrice,BigDecimal bigDecimal) {
       bigDecimal = uiChartPrice.firstPrice();
       textViewResult.setText(""+bigDecimal);
}

이렇게 인자에 값을 다시 할당하는 건 좋지않습니다. 인자명을 바꾸시는게 안전합니다.
private void dataProcess(UiChartPrice uiChartPrice,BigDecimal bigDecimal) {
       BigDecimal firstPrice = uiChartPrice.firstPrice();
       textViewResult.setText(""+firstPrice);
}

위처럼 변수명을 변경하게 되면 bigDecimal 인자가 사용되고 있지 않을 걸 보실 수 있을 겁니다. 메소드에서 어떤 기능을 수행하고 싶은지 의도를 명확하게 하는 코드를 사용하세요. 현재 메소드의 내용으로는 뭐를 하려고 하시는지 알기가 어렵습니다.
...