質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

キャスト

キャストとは、オブジェクトの型の変換が許可された場合に、明白に別の型への変換を行うプロセスのことです。

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

Q&A

解決済

3回答

22211閲覧

LinkedHashMapを指定クラスにキャスト出来ない

rontec

総合スコア169

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

キャスト

キャストとは、オブジェクトの型の変換が許可された場合に、明白に別の型への変換を行うプロセスのことです。

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

0グッド

1クリップ

投稿2015/11/12 01:10

編集2015/11/12 08:43

HTTP Client Library for Javaで通信したInstagramのAPIのレスポンスの受け皿クラスを作成し、キャストしようと企んでいます。

以下キャスト用クラスです。

java

1@Getter 2public class クラスA extends GenericJson{ 3 @Key("pagination") 4 private HashMap<String, String> pagination; 5 @Key("meta") 6 private HashMap<String, String> meta; 7 @Key("data") 8 private List<DataBreakdown> data; 9 1011 12 // inner class 13 @Getter 14 class DataBreakdown{ 15 @Key("attribution") 16 private Object attribution; 17 @Key("tags") 18 private List<String> tsgs; 19 @Key("type") 20 private String type; 21 @Key("location") 22 private Object location; 23 @Key("comments") 24 private HashMap<String, Object> comments; 25 @Key("filter") 26 private Object filter; 27 @Key("created_time") 28 private Object created_time; 29 @Key("link") 30 private String link; 31 @Key("likes") 32 private HashMap<String, Object> likes; 33 @Key("images") 34 private HashMap<String, Object> images; 35 @Key("users_in_photo") 36 private Object users_in_photo; 37 @Key("caption") 38 private HashMap<String, Object> caption; 39 @Key("user_has_liked") 40 private Object user_has_liked; 41 @Key("id") 42 private String id; 43 @Key("user") 44 private HashMap<String, String> user; 45 }

実行したところ、下記拡張forでエラーになります。

java

1 public List<HashMap<String, Object>> getInstagramImageData() { 2 List<HashMap<String, Object>> instagramImageList = new ArrayList<>(); 3 4 for (DataBreakdown oneData : data) { 5 HashMap<String, Object> instagramImageData = new HashMap<>(); 6 instagramImageData.put("images", oneData.getImages()); 7 instagramImageList.add(instagramImageData); 8 } 9 return instagramImageList; 10 }
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to クラスA$DataBreakdown ※クラス名は「クラスA」に変換しています

@Key("data")をHashMap<String, Object>にマッピング、objectで扱う分には特に問題ありません。
ただそうすると @Key("data")の中身をjavaで扱いづらいので、なんとか作成したクラスにマッピングしたいのですが、LinkedHashMap のキャストがうまくいかないようです。

javaの経験が乏しく具体的に何をどうしたら解決することが出来るのか分かりません。
詳しい方がいらしたら、アドバイスをお願いいたします。

なお、instagramのレスポンスを一部抜粋した内容は以下のとおりです。

"data": [ { "attribution": null, "tags": [ "北欧", "chihuahua", "チワワ", "熊", "dog", "bear", "クマ", "pet", "インテリア", "犬との暮らし", "マンション", "ビーズクッション", "暮らし", "愛犬" ], "type": "image", "location": null, "comments": { "count": 1, "data": [ { "created_time": "1446716528", ~

以下呼び出し元

java

1 public クラスA Access(String url) throws IOException { 2 クラスA instagramData; 3 try { 4 HttpRequestFactory requestFactory = httpTransport.createRequestFactory(); 5 GenericUrl genericUrl = new GenericUrl(url); 6 HttpRequest req = requestFactory.buildGetRequest(genericUrl); 7 HttpResponse res = req.execute(); 8 9 try { 10 instagramData = asInstagramObject(res.parseAsString()); 11 } finally { 12 res.disconnect(); 13 } 14 } finally { 15 httpTransport.shutdown(); 16 } 17 return instagramData; 18 } 19 20 // Json文字列をInstagramObjectに変換 21 private クラスA asInstagramObject(String stringJson) throws IOException { 22 ObjectMapper mapper = new ObjectMapper(); 23 クラスA json = mapper.readValue(stringJson, クラスA.class); 24 return json; 25 }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

swordone

2015/11/12 01:27

DataBreakDownクラスの内容と、拡張for文の後に何を返しているのか(つまり、何を返すメソッドの中に書いているのか)、またそのメソッドはどこに属しているのかなどが不明です。省略しているメソッドを可能な限り書いてください。
rontec

2015/11/12 01:49

ご返信ありがとうございます。 省略していたコードを追加させて頂きました。 もし何かお気づきの点があれば、ご教示いただければ幸いです。
guest

回答3

0

ベストアンサー

HTTP Client Library for Javaの1.20.0で試したところ、以下のコードでうまくいきました。

変更点は
1.@Getterを削除した。
2.自分でFactoryを作った。
3.class DataBreakdownpublic static class DataBreakdownに直した。
です。

Factoryを自分で作って実行していることもあってか、
同じ現象は発生しませんでしたが、少なくとも
3.を実施しないと、例外が発生して動作しませんでした。

lan

1import com.google.api.client.json.GenericJson; 2import com.google.api.client.json.JsonObjectParser; 3import com.google.api.client.json.jackson.JacksonFactory; 4import com.google.api.client.util.Key; 5 6public class クラスA extends GenericJson { 7 @Key("data") 8 private List<DataBreakdown> data; 9 10 // inner class 11 public static class DataBreakdown { 12 @Key("attribution") 13 private Object attribution; 14 @Key("tags") 15 private List<String> tsgs; 16 @Key("type") 17 private String type; 18 @Key("location") 19 private Object location; 20 } 21 22 public List<HashMap<String, Object>> getInstagramImageData() { 23 List<HashMap<String, Object>> instagramImageList = new ArrayList<>(); 24 25 for (DataBreakdown oneData : data) { 26 System.out.println(oneData.type); 27 28 } 29 return instagramImageList; 30 } 31 32 public static void main(String[] args) { 33 JacksonFactory factory = new JacksonFactory(); 34 JsonObjectParser objectParser = factory.createJsonObjectParser(); 35 try { 36 クラスA a = objectParser.parseAndClose(new FileReader("input.json"), クラスA.class); 37 a.getInstagramImageData(); 38 } catch (IOException e) { 39 e.printStackTrace(); 40 } 41 } 42}

input.jsonの中身

lang

1{ 2 "data": [ 3 { 4 "attribution": null, 5 "tags": [ 6 "北欧" 7 ], 8 "type": "image", 9 "location": null 10 }, 11 { 12 "attribution": null, 13 "tags": [ 14 "北欧" 15 ], 16 "type": "image", 17 "location": null 18 } 19 ] 20}

###推測される原因
おそらく、ライブラリ側からパース時にDataBreakdownなどのインスタンスをnewするために、
ライブラリからアクセス可能なデフォルトコンストラクタが必要なことが原因です。
public staticを付けない場合に、以下のような例外が発生したためです。
試してはいませんが、内部クラスをやめて、publicなクラスにしても成功すると思います。

Exception in thread "main" java.lang.IllegalArgumentException: at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:880) at com.google.api.client.json.JsonParser.parse(JsonParser.java:381) at com.google.api.client.json.JsonParser.parse(JsonParser.java:354) at com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:98) at com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:92) at sandbox.httpclient.クラスA.main(クラスA.java:46) -(略)- Caused by: java.lang.IllegalArgumentException: unable to create new instance of class sandbox.httpclient.クラスA$DataBreakdown because it is not static and possibly because it is not public at com.google.api.client.util.Types.handleExceptionForNewInstance(Types.java:165) at com.google.api.client.util.Types.newInstance(Types.java:120) at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:763) ... 9 more -(略)-

[追記]実際にリクエストからパースする場合

以下のコードで、実際にHTTPリクエストを送って結果をパースすることができました。
変更点は、
1.Factoryを使ってJsonObjectParserを追加した。
2.asInstagramObject(res.parseAsString())していたのを、parseAs(クラスA.class)に変更した。
3.動作確認のためにinstagramData.getInstagramImageData();を追加した。

lang

1try { 2 HttpRequestFactory requestFactory = httpTransport.createRequestFactory(); 3 GenericUrl genericUrl = new GenericUrl(url); 4 HttpRequest req = requestFactory.buildGetRequest(genericUrl); 5 req.setParser(new JacksonFactory().createJsonObjectParser()); // 1.追加 6 HttpResponse res = req.execute(); 7 8 try { 9 instagramData = res.parseAs(クラスA.class); // 2.変更 10 instagramData.getInstagramImageData(); // 3.動作確認のために追加 11 } finally { 12 res.disconnect(); 13 } 14} finally { 15 httpTransport.shutdown(); 16}

動作確認に使ったjarは、以下の通りです。

commons-logging-1.1.1.jar google-http-client-1.20.0.jar google-http-client-jackson-1.20.0.jar lombok.jar jackson-core-asl-1.9.11.jar

投稿2015/11/12 06:52

編集2015/11/12 11:24
eripong

総合スコア1546

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rontec

2015/11/12 07:42

わざわざ試作まで頂きありがとうございます。 再度私の方でも試してみましたが、やはりキャストエラーがとれませんでした(public staticをつけても同様です)。 ご丁寧にコードまで頂き恐縮ですが、問題だと思っていたinnerClassでの@keyが問題なく出来たとのことなので、私が問題と考えていなかったところに、何か問題があるのかもしれません。 一度パースしたobjectに対しても@keyが使える件は参考になりました、ありがとうございます。
eripong

2015/11/12 07:52

そうですか。 @GetterはLombokだと思ってそれも追加してみましたが、 それでもこちらではうまく行きました。 マッピング自体はできそうなのですが、パースはどのように行っていますか? その部分のコードを教えていただければ、こちらでも試してみます。
rontec

2015/11/12 08:46

ご丁寧にありがとうございます。 @GetterはLombokのご認識で間違いございません。 こちらにコードを記載するとブロック表示されないようなので、一番最初の質問の中に「以下呼び出し元」というコメントの直下に、パースをしているコードを追加させて頂きました。 様々なサイトを参考にしつつ、試し試し勉強しながら作っているので、おかしな部分もあり読みづらいかと思いますが、よろしければよろしくお願いいたします。
eripong

2015/11/12 11:15

こちらで変更して、動作はするようになったコードを、回答に追記しました。 リクエストをStringに読み込んでObjectMapperでパースするのではなく、 HTTP Client Library for Javaの機能だけで実現しましたが、 ObjectMapperを使う必要はありますか? そもそも、ObjectMapperがどのライブラリのクラスか分かりませんでした。
rontec

2015/11/12 13:09

使用したライブラリも記載するべきでした、失礼いたしました。 色々ご相談に乗って頂き、本当にありがとうございます。 ご指摘の通り実装したところ、無事キャストして実行することが出来ました。 ObjectMapperを使用した理由は、parseAsを使用してもうまくいかず、parseAsStringをとObjectMapperを使用したサイトの方法を真似たところ、とりあえず動いたのでそのままにしたという、お恥ずかしいことにあまり理解もせず使用していた部分です。 1からJavaのコードを作成するのは実に9年ぶりなので不明点も多く、今回の件も事故解決出来ずteratailで相談させて頂いておりました。 ありがとうございました。
eripong

2015/11/12 13:16

9年前だと、Javaもだいぶ違いますよね。 解決してよかったです。
guest

0

その拡張forでその例外が出ているとしたら、考えられる可能性は、
List<DataBreakdown> dataの要素に、実体がLinkedHashMapのオブジェクトが入っている、ということだと思われます。
このdataをどうやって生成しているのかわかりませんが、
APIで型を指定しないListを作ったとして、それをList<DataBreakdown>に代入してもコンパイルエラーにはなりません。Javaの総称型の特性上、こういうことが可能になってしまっています。
dataが持つListは型をDataBreakdownで指定しているため、取得などの時に要素をDataBreakdownにキャストします。このとき、要素の実体が実はLinkedHashMapであった場合、提示されたような例外が発生します。
このAPIの事をよく知らないので、もう一度検討しなおしたほうがいいとしかアドバイスできないのが辛いです…

投稿2015/11/12 02:12

swordone

総合スコア20669

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rontec

2015/11/12 02:30

はい、どうやらinstagramのdataの中身がLinkedHashMapになっているようで、どうにかこれをjavaで扱い易い形に変更したいと考えております。 レスポンスの中身が例えば<String,String>で一定であれば問題ないのですが、keyによっては階層が深いので、こちらでクラスを用意せざる得ないところです。 現状フロントのJSに投げた後、JSであれば連想配列として簡単に扱えるので対応できているのですが、javaとしてこのような際どう扱えばよいか知っておきたいと思い、今回質問させて頂いておりました。 ご対応ありがとうございました。 ご指摘からもDataBreakdownの定義方法がやはりよろしくないことがわかりました。
swordone

2015/11/12 02:47

JavaScriptの連想配列もJavaのMapも基本的な発想は同じで、何かを鍵として与えるとそれに関連づいた物が取得できる、という点で共通しています。 おそらくdataに入っている本来の形はList<LinkedHashMap<String, Object>>なのではないでしょうか? そうだと仮定して、分解する際には、キーによって関連づいている型がある程度決まっていると思われるので、キーを使ってObjectを取得した後、値をキーをもとにキャストすればいいのではないでしょうか? 例えばレスポンスでtagsというキーにはList<String>が対応し、commentsにはMap<String, Object>が対応していると思われます。それぞれにあったキャストを適用して値を分解し、格納していくのがいいのではないでしょうか? あくまで推測ですし、かなり面倒そうですが。
anonymouskawa

2015/11/12 02:52

rontecさんとswordoneさんで認識があっていない気がします。 for (DataBreakdown oneData : data) { この部分のdataの型がおかしいのではないか?と疑っているので、 ブレークポイントをつけてデバッグモードでdataを確認してみてはいかがですか?
rontec

2015/11/12 06:57 編集

ご指摘ありがとうございます。 dataの中身は下記のような、LinkedHashMapになっております。 argius様への返信にも書いたのですが、@keyアノテーションを調べたところHTTP Client Library for Javaで通信したレスポンスをパースする際にkeyで指定した値を拾うようなので、dataで一度パースされていたことが原因で機能しなかったのではないかと推測しております。 ```java { attribution=null, tags=[rabbits, triu?is, haas, coello, lucky, likes, rabbit, follow, conill, ??, like, kanin, okeoyibo, follows, 兔仔, 兔子, terwelu, tav?an, ウサギ, трус?к, 兔], type=image, location=null, comments={count=0, data=[]}, filter=Normal, created_time=1446773288, link=https://, likes={count=1, data=[{username=homey_the_bunny, profile_picture=http://, id=XXXXXXXXX, full_name=Ho Mey Lee}]}, images={ low_resolution={url=XXXXXXXXXXX, width=320, height=320}, thumbnail={url=XXXXXXXXXXXXX, width=150, height=150}, standard_resolution={url=XXXXXXXXXXX, width=640, height=640} }, users_in_photo=[], caption={ created_time=1446773288, text=?lucky : good morning ?#rabbit #rabbits #lucky #ウサギ #?? #兔 #兔子 #兔仔 #follow #follows #like #likes #Tav?an #Kanin #Terwelu #Coello #Conill #Haas #Трус?к #Triu?is #OkeOyibo, from={username=rabbit_lucky_, profile_picture=XXXXXXXXXXXXx, id=XXXXXX, full_name=?Lucky? }, id=XXXXXXXXXXXXXXXXX, user_has_liked=false, id=XXXXXXXXXXXXXXXX, user={ username=rabbit_lucky_, profile_picture=XXXXXXXXXXXXXXXXXX, id=XXXXXXXXXXXX, full_name=?Lucky? } } ``` ※公開されているので問題ないとは思うのですが、一応該当instagramデータが特定出来そうな部分は潰しております。
guest

0

良い方法とは思えませんが、欲しい項目を一つ一つputする方法を取りました。
欲しい項目の階層が深いとき、キャストにキャストを重ねているため褒められた方法でないのは確かです。
こういう時、ほんとどうしたら良いのでしょうね。

java

1 for (HashMap<String, Object> oneData : data) { 2 HashMap<String, Object> castData = new HashMap<>(); 3 castData.put("tags", oneData.get("tags")); 4 castData.put("type", oneData.get("type")); 5 castData.put("link", oneData.get("link")); 6 castData.put("likes_count", ((HashMap<String, Object>)oneData.get("likes")).get("count")); 7 castData.put("images_url_low", ((HashMap<String, HashMap<String, String>>)oneData.get("images")).get("low_resolution").get("url")); 8 castData.put("images_url_standard", ((HashMap<String, HashMap<String, String>>)oneData.get("images")).get("standard_resolution").get("url")); 9 castData.put("caption", oneData.get("caption")); 10 castData.put("id", oneData.get("id")); 11// castData.put("user", oneData.get("user")); 12 instagramImageList.add(castData); 13 }

投稿2015/11/12 04:18

rontec

総合スコア169

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

argius

2015/11/12 04:24

HTTP Client Libraryってこれですか?→google-http-java-client このページ tree-tips: google-http-java-clientで簡単httpリクエスト! | java http://www.tree-tips.com/java/googleHttpJavaClient/ を見る限りでは、ちゃんとマッピングできているように見えます。 どのような情報を参考に作られたのでしょうか? それが間違っている可能性はありませんか?
rontec

2015/11/12 05:43

書き込みありがとうございます。 はい、貼っていただいたURLのもので間違いございません。 私も言語を問わず様々なサイトを参照したので一概に、このページですと言えないのですが、このページも参考にしたことがありますが、うまくいきませんでした。 おそらくですが、instagramのレスポンスを正しくキャスト出来るようなクラスを私が作れていないのだと思います。 また当初の質問に関しては、@Keyのアノテーションを調べたところ、パース後にキャストするようなことが書いてあったので、一度dataでパースした後の中身を更に@keyを使用しても意味なかったのではと考えております。
argius

2015/11/12 08:18

返信ありがとうございます。 私も興味があるので調べてみようと思いましたが、eripongさんの回答が参考になりそうなので、そちらにお任せします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問