Java object obj
に属性_obj.attr1
_、_obj.attr2
_などがあります。属性は、場合によっては追加の間接レベルを介してアクセスされます:obj.getAttr1()
、obj.getAttr2()
、パブリックでない場合。
チャレンジ:オブジェクトを受け取り、_Map<String, Object>
_を返す関数が必要です。キーは文字列_"attr1"
_、_"attr2"
_などで、値は対応するオブジェクト_obj.attr1
_、_obj.attr2
_。関数は次のように呼び出されると思います
toMap(obj)
、toMap(obj, "attr1", "attr3")
(ここで_attr1
_および_attr3
_はobj
の属性のサブセットです)、toMap(obj, "getAttr1", "getAttr3")
。Javaのイントロスペクションについてあまり知りません。Javaでそれをどのように行うのですか?
現在、私が気にしている各オブジェクトタイプに特化したtoMap()
実装があり、それは定型すぎるものです。
[〜#〜] note [〜#〜]:Pythonを知っている人のために、私は_obj.__dict__
_のようなものが欲しい。または、サブセットバリアントの場合はdict((attr, obj.__getattribute__(attr)) for attr in attr_list)
。
これにはJavaBeansイントロスペクションを使用できます。 Java.beans.Introspector
クラス:
public static Map<String, Object> introspect(Object obj) throws Exception {
Map<String, Object> result = new HashMap<String, Object>();
BeanInfo info = Introspector.getBeanInfo(obj.getClass());
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
Method reader = pd.getReadMethod();
if (reader != null)
result.put(pd.getName(), reader.invoke(obj));
}
return result;
}
大きな警告:私のコードはゲッターメソッドのみを扱います。裸のフィールドは見つかりません。フィールドについては、highlycaffeinatedの回答をご覧ください。 :-)(おそらく、2つのアプローチを組み合わせたいと思うでしょう。)
ユーザーへの別の方法 JacksonObjectMapper
はconvertValue
exです。
ObjectMapper m = new ObjectMapper();
Map<String,Object> mappedObject = m.convertValue(myObject,Map.class);
Apache Commons BeanUtilsを使用します: http://commons.Apache.org/beanutils/ 。
イントロスペクションを使用してBeanのプロパティを取得および配置するMap for JavaBeansの実装:
Map<Object, Object> introspected = new org.Apache.commons.beanutils.BeanMap(object);
注:APIがMap<Object, Object>
(1.9.0以降)を返すという事実にもかかわらず、返されるマップ内のキーの実際のクラスはJava.lang.String
です
これは大まかな概算です。うまくいけば、正しい方向を示すのに十分です。
public Map<String, Object> getMap(Object o) {
Map<String, Object> result = new HashMap<String, Object>();
Field[] declaredFields = o.getClass().getDeclaredFields();
for (Field field : declaredFields) {
result.put(field.getName(), field.get(o));
}
return result;
}
これを行うための本当にeasy方法があります。
Jackson JSON libを使用して、オブジェクトをJSONに変換します。
次に、JSONを読み取り、マップに変換します。
マップには、必要なものがすべて含まれます。
ここに4ライナーがあります
ObjectMapper om = new ObjectMapper();
StringWriter sw = new StringWriter();
om.writeValue(object, sw);
Map<String, Object> map = om.readValue(sw.toString(), Map.class);
もちろん、これは再帰的であり、必要な場合にマップのマップを作成します
これらはネストされたプロパティでは機能せず、オブジェクトマッパーは公平な仕事を行います。ただし、マップで表示するすべてのフィールドにすべての値を設定する必要があります。プロパティの。残念ながら、あなたは次のようなことをしなければなりません、それはただ考えを与えるためのドラフトにすぎません。
/*
* returns fields that have getter/setters including nested fields as
* field0, objA.field1, objA.objB.field2, ...
* to take care of recursive duplicates,
* simply use a set<Class> to track which classes
* have already been traversed
*/
public static void getBeanUtilsNestedFields(String prefix,
Class clazz, List<String> nestedFieldNames) throws Exception {
PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(clazz);
for(PropertyDescriptor descr : descriptors){
// if you want values, use: descr.getValue(attributeName)
if(descr.getPropertyType().getName().equals("Java.lang.Class")){
continue;
}
// a primitive, a CharSequence(String), Number, Date, URI, URL, Locale, Class, or corresponding array
// or add more like UUID or other types
if(!BeanUtils.isSimpleProperty(descr.getPropertyType())){
Field collectionfield = clazz.getDeclaredField(descr.getName());
if(collectionfield.getGenericType() instanceof ParameterizedType){
ParameterizedType integerListType = (ParameterizedType) collectionfield.getGenericType();
Class<?> actualClazz = (Class<?>) integerListType.getActualTypeArguments()[0];
getBeanUtilsNestedFields(descr.getName(), actualClazz, nestedFieldNames);
}
else{ // or a complex custom type to get nested fields
getBeanUtilsNestedFields(descr.getName(), descr.getPropertyType(), nestedFieldNames);
}
}
else{
nestedFieldNames.add(prefix.concat(".").concat(descr.getDisplayName()));
}
}
}
mavenの依存関係
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
....
ObjectMapper m = new ObjectMapper();
Map<String,Object> mappedObject = m.convertValue(myObject,Map.class);
jSR310 New Date/Time APIの場合、改善が必要な問題がいくつかあります。例:
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.Test;
import Java.sql.Timestamp;
import Java.time.LocalDateTime;
import Java.time.ZoneId;
import Java.util.Map;
@Data
@NoArgsConstructor
public class QueryConditionBuilder
{
LocalDateTime startTime;
LocalDateTime endTime;
Long nodeId;
Long fsId;
Long memId;
Long ifCardId;
private QueryConditionBuilder(QueryConditionBuilder.Builder builder) {
setStartTime(builder.startTime);
setEndTime(builder.endTime);
setNodeId(builder.nodeId);
setFsId(builder.fsId);
setMemId(builder.memId);
setIfCardId(builder.ifCardId);
}
public static QueryConditionBuilder.Builder newBuilder() {
return new QueryConditionBuilder.Builder();
}
public static QueryConditionBuilder newEmptyBuilder() {
return new QueryConditionBuilder.Builder().build();
}
public Map<String,Object> toFilter()
{
Map<String,Object> filter = new ObjectMapper().convertValue(this,Map.class);
System.out.printf("查询条件:%s\n", JSON.toJSONString(filter));
return filter;
}
public static final class Builder {
private LocalDateTime startTime;
private LocalDateTime endTime;
private Long nodeId = null;
private Long fsId = null;
private Long memId =null;
private Long ifCardId = null;
private Builder() {
}
public QueryConditionBuilder.Builder withStartTime(LocalDateTime val) {
startTime = val;
return this;
}
public QueryConditionBuilder.Builder withEndTime(LocalDateTime val) {
endTime = val;
return this;
}
public QueryConditionBuilder.Builder withNodeId(Long val) {
nodeId = val;
return this;
}
public QueryConditionBuilder.Builder withFsId(Long val) {
fsId = val;
return this;
}
public QueryConditionBuilder.Builder withMemId(Long val) {
memId = val;
return this;
}
public QueryConditionBuilder.Builder withIfCardId(Long val) {
ifCardId = val;
return this;
}
public QueryConditionBuilder build() {
return new QueryConditionBuilder(this);
}
}
@Test
public void test()
{
LocalDateTime now = LocalDateTime.now(ZoneId.of("+8"));
LocalDateTime yesterday = now.plusHours(-24);
Map<String, Object> condition = QueryConditionBuilder.newBuilder()
.withStartTime(yesterday)
.withEndTime(now)
.build().toFilter();
System.out.println(condition);
}
}
expects(擬似コード):
查询条件:{"startTime":{"2019-07-15T20:43:15"},"endTime":{"2019-07-16T20:43:15"}
{startTime={2019-07-15T20:43:15}, endTime={"2019-07-16T20:43:15"}, nodeId=null, fsId=null, memId=null, ifCardId=null}
代わりに、私はこれらを得た:
查询条件:{"startTime":{"dayOfMonth":15,"dayOfWeek":"MONDAY","dayOfYear":196,"hour":20,"minute":38,"month":"JULY","monthValue":7,"nano":263000000,"year":2019,"second":12,"chronology":{"id":"ISO","calendarType":"iso8601"}},"endTime":{"dayOfMonth":16,"dayOfWeek":"TUESDAY","dayOfYear":197,"hour":20,"minute":38,"month":"JULY","monthValue":7,"nano":263000000,"year":2019,"second":12,"chronology":{"id":"ISO","calendarType":"iso8601"}}}
{startTime={dayOfMonth=15, dayOfWeek=MONDAY, dayOfYear=196, hour=20, minute=38, month=JULY, monthValue=7, nano=263000000, year=2019, second=12, chronology={id=ISO, calendarType=iso8601}}, endTime={dayOfMonth=16, dayOfWeek=TUESDAY, dayOfYear=197, hour=20, minute=38, month=JULY, monthValue=7, nano=263000000, year=2019, second=12, chronology={id=ISO, calendarType=iso8601}}, nodeId=null, fsId=null, memId=null, ifCardId=null}
いくつかの調査の後、効果的なトリックが見つかりました、
ObjectMapper mapper = new ObjectMapper();
JavaTimeModule module = new JavaTimeModule();
//https://github.com/networknt/light-4j/issues/82
mapper.registerModule(module);
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
Map<String,Object> filter = mapper.convertValue(this,Map.class);
System.out.printf("查询条件:%s\n", JSON.toJSONString(filter));
return filter;
出力:
查询条件:{"startTime":"2019-07-15T21:29:13.711","endTime":"2019-07-16T21:29:13.711"}
{startTime=2019-07-15T21:29:13.711, endTime=2019-07-16T21:29:13.711, nodeId=null, fsId=null, memId=null, ifCardId=null}
MyBatisの動的クエリに上記のコードを使用しました
例えば。
/***
* 查询文件系统使用率
* @param condition
* @return
*/
LinkedList<SnmpFileSystemUsage> queryFileSystemUsage(Map<String,Object> condition);
List<SnmpFileSystemUsage> fooBar()
{
return snmpBaseMapper.queryFileSystemUsage(QueryConditionBuilder
.newBuilder()
.withNodeId(nodeId)
.build()
.toFilter());
}