解决Mongo插入时候 Invalid BSON field 问题。

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

版本:

		
            org.mongodb
            mongodb-driver
            3.6.3
        

2、原因分析

java.lang.IllegalArgumentException 异常,说明该字段就不合法,没法插入到mongoDB中

  1. 直接看报错内容:
    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;
    }
  1. 点开进入 validate方法:
package org.bson;

public interface FieldNameValidator {

    boolean validate(String fieldName);

    FieldNameValidator getValidatorForField(String fieldName);
}

3.查看validate的实现类。control + 5。
解决Mongo插入时候 Invalid BSON field 问题。_第1张图片

  1. 打开CollectibleDocumentFieldNameValidator,有个validate方法,直接用就行

3、解决方法

换该字段名称,或者干脆剔除。

如果这个字段是你自定义的,那还好说,但是,假如是不确定的,比如我报错的是该Java对象里面有个 Map ,我是Map的key出了问题。
那么解决方法只能是先判断字段名称是否合法,不合法的话就剔除该字段。

  1. 在Document insert的时候,验证并去掉。注意 org.bson.Document 底层就是一个Map
public class Document implements Map, Serializable, Bson 
  1. 自定义 insert 方法:(注意 :removeTheIllegalKeyForStore )
public void insert(MongoCollection coll, Document Document) {

        try {
            coll.insertOne(Document);
        } catch (java.lang.IllegalArgumentException e) {
            LOGGER.error("{}",e);
            Document newDocument = removeTheIllegalKeyForStore(Document);
            coll.insertOne(newDocument);
        }
    }
  1. 自定义 removeTheIllegalKeyForStore 方法

    public static Document removeTheIllegalKeyForStore(Document document){
        /*
            1、 new document
         */
        Document newDocument = new Document();

        for (Map.Entry 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 map = (Map) entryValue;
                Map newMap = Maps.newHashMap();

                /*
                    3. get the Map key type
                 */
                Class keyClazz = null;
                Set 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 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;
    }
 
  
  1. 衍生,写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
         */
        List 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 map = (Map) mapField.get(instance);
                Set 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;
    }
  1. 递归的问题暂时没考虑,先这样。

你可能感兴趣的:(java)