1、先看异常
java.lang.IllegalArgumentException: Invalid BSON field name google_web-**-beauty.m
at org.bson.AbstractBsonWriter.writeName(AbstractBsonWriter.java:532)
at org.bson.codecs.DocumentCodec.writeMap(DocumentCodec.java:198)
at org.bson.codecs.DocumentCodec.writeValue(DocumentCodec.java:182)
at org.bson.codecs.DocumentCodec.writeMap(DocumentCodec.java:199)
at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:141)
at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:45)
at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:63)
at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:29)
...
版本:
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>3.6.3</version>
</dependency>
2、原因分析
java.lang.IllegalArgumentException 异常,说明该字段就不合法,没法插入到mongoDB中
- 直接看报错内容:
AbstractBsonWriter.java 532行
@Override
public void writeName(final String name) {
notNull("name", name);
if (state != State.NAME) {
throwInvalidState("WriteName", State.NAME);
}
if (!fieldNameValidatorStack.peek().validate(name)) {
throw new IllegalArgumentException(format("Invalid BSON field name %s", name));
}
doWriteName(name);
context.name = name;
state = State.VALUE;
}
- 点开进入 validate方法:
package org.bson;
public interface FieldNameValidator {
boolean validate(String fieldName);
FieldNameValidator getValidatorForField(String fieldName);
}
3.查看validate的实现类。control + 5。
- 打开CollectibleDocumentFieldNameValidator,有个validate方法,直接用就行
3、解决方法
换该字段名称,或者干脆剔除。
如果这个字段是你自定义的,那还好说,但是,假如是不确定的,比如我报错的是该Java对象里面有个 Map<String,Double> ,我是Map的key出了问题。
那么解决方法只能是先判断字段名称是否合法,不合法的话就剔除该字段。
- 在Document insert的时候,验证并去掉。注意 org.bson.Document 底层就是一个Map
public class Document implements Map<String, Object>, Serializable, Bson
- 自定义 insert 方法:(注意 :removeTheIllegalKeyForStore )
public void insert(MongoCollection<Document> coll, Document Document) {
try {
coll.insertOne(Document);
} catch (java.lang.IllegalArgumentException e) {
LOGGER.error("{}",e);
Document newDocument = removeTheIllegalKeyForStore(Document);
coll.insertOne(newDocument);
}
}
- 自定义 removeTheIllegalKeyForStore 方法
public static Document removeTheIllegalKeyForStore(Document document){
/*
1、 new document
*/
Document newDocument = new Document();
for (Map.Entry<String, Object> entry :
document.entrySet()) {
Object entryValue = entry.getValue();
if(null == entryValue){
continue;
}
Class<?> clazz = entryValue.getClass();
/*
2、 if the entryValue class is Map,
validate the Map key
*/
if (Map.class.isAssignableFrom(clazz)) {
Map<Object, Object> map = (Map<Object, Object>) entryValue;
Map<Object, Object> newMap = Maps.newHashMap();
/*
3. get the Map key type
*/
Class<?> keyClazz = null;
Set<Object> keySet = map.keySet();
for (Object k : keySet) {
keyClazz = k.getClass();
break;//只需要判断第一个元素
}
/*
4. if the Map key type is "java.lang.String"
*/
if("java.lang.String".equals(keyClazz.getName())){
for (Map.Entry<Object, Object> keyNameEntry :
map.entrySet()) {
String realKeyName = String.valueOf(keyNameEntry.getKey());
boolean isValidate = new CollectibleDocumentFieldNameValidator().validate(realKeyName);
/*
5. validate
*/
if (isValidate) {
newMap.put(keyNameEntry.getKey(),keyNameEntry.getValue());
}else{
LOGGER.error("realKeyName -> {} is illegal ... remove it",realKeyName);
}
}
entryValue = newMap;
}
}
newDocument.put(entry.getKey(),entryValue);
}
return newDocument;
}
- 衍生,写4方法之前,当时还不知道Document的底层是个Map,知道之后确实直接Document在insert处理就OK方便多了。下面这个方法是针对insert前,也就是未转化成Document前,针对JAVA对象,判断Java对象内的Map是否合法,不合法就去掉。
public static Object removeTheIllegalKeyForStore(Class clazz,Object instance) {
/*
1. find the field which type is Map<String,?>
*/
List<Field> mapFieldList = Lists.newArrayList();
Field[] fields = clazz.getDeclaredFields();
if(null != fields){
for (Field field :
fields) {
Class<?> fieldClazz = field.getType();
if(Map.class.isAssignableFrom(fieldClazz) &&
ParameterizedTypeImpl.class.isAssignableFrom(field.getGenericType().getClass())){
ParameterizedTypeImpl parameterizedTypeImpl = (ParameterizedTypeImpl) field.getGenericType();
Type keyType = parameterizedTypeImpl.getActualTypeArguments()[0];
if(keyType.getTypeName().equalsIgnoreCase("java.lang.String")){
System.out.println(field.getName());
mapFieldList.add(field);
}
}
}
}
/*
2. validate the map key whether it can be store
*/
for (Field mapField :
mapFieldList) {
try {
mapField.setAccessible(true);
Map<String,Object> map = (Map<String, Object>) mapField.get(instance);
Set<String> illegalKeys = Sets.newHashSet();
for (String key :
map.keySet()) {
boolean isValidate = new CollectibleDocumentFieldNameValidator().validate(key);
if(!isValidate){
illegalKeys.add(key);
}
}
for (String illegalKey :
illegalKeys) {
LOGGER.error("remove illegalKey -> {}",illegalKey);
map.remove(illegalKey);
}
mapField.set(instance,map);
} catch (IllegalAccessException e) {
LOGGER.error("{}",e);
}
}
return instance;
}
- 递归的问题暂时没考虑,先这样。