是否有一个常见的Java方法来修剪对象图中的每个字符串?

时间:2014-02-19 22:41:23

标签: java string reflection object-graph

我希望修剪属于对象图的所有字符串。

所以我有一个像这样的对象图

 RootElement
   - name (String)
   - adjective (String)
   - items ArrayOfItems
     - getItems (List<Item>)
       - get(i) (Item)
       Item
         - name (String)
         - value (double)
         - alias (String)
         - references ArrayOfReferences
           - getReferences (List<Reference>)
             - get(i) (Reference)
             Reference
               - prop1 (String)
               - prop2 (Integer)
               - prop3 (String)

对象图中表示的每个类的每个属性都有一个get和set对。理想情况下,String类型的每个字段都会被修剪,包括枚举集合中包含的任何子对象。对象图中没有包含循环。

是否有任何java库实现某种通用对象图访问者模式或String \ Reflection实用程序库来执行此操作?

执行此操作的外部第三方库也没问题,它不必是标准java库的一部分。

4 个答案:

答案 0 :(得分:10)

不,没有内置的遍历这样的东西,并且记住Java String是不可变的,所以你不能实际修剪到位 - 你必须修剪和替换。某些对象可能不允许修改其String变量。

答案 1 :(得分:5)

以下是我使用Java Reflection API构建的解决方案的说明。我在下面发布了工作代码(及其url到github)。该解决方案主要使用:

  1. Java Reflection API
  2. 独立处理Java Collections
  3. 递归
  4. 首先,我使用了Introspector来查看readMethods的{​​{1}},省略了为Class定义的方法

    Object

    1. 如果for (PropertyDescriptor propertyDescriptor : Introspector .getBeanInfo(c, Object.class).getPropertyDescriptors()) { Method method = propertyDescriptor.getReadMethod(); 的当前级别属于Property
    2. 类型
    3. 如果是String属性数组
    4. 如果是Object数组
    5. 如果是一种Java String
    6. 为具有特殊条件的Collection单独展示处理其Mapkeys
    7. 此实用程序使用values遍历具有规范语法Java Reflection APIgetters的对象图,并以递归方式修剪setters图中遇到的所有Strings

      代码

      这个包含主要测试类(和自定义数据类型/ pojos)的整个util类是here on my github

      用法:

      Object

      Util方法:

      myObj = (MyObject) SpaceUtil.trimReflective(myObj);
      

      测试

      我还有一个测试类,它创建了一个相对复杂的对象。测试类有不同的场景,包括:

      1. public static Object trimReflective(Object object) throws Exception { if (object == null) return null; Class<? extends Object> c = object.getClass(); try { // Introspector usage to pick the getters conveniently thereby // excluding the Object getters for (PropertyDescriptor propertyDescriptor : Introspector .getBeanInfo(c, Object.class).getPropertyDescriptors()) { Method method = propertyDescriptor.getReadMethod(); String name = method.getName(); // If the current level of Property is of type String if (method.getReturnType().equals(String.class)) { String property = (String) method.invoke(object); if (property != null) { Method setter = c.getMethod("set" + name.substring(3), new Class<?>[] { String.class }); if (setter != null) // Setter to trim and set the trimmed String value setter.invoke(object, property.trim()); } } // If an Object Array of Properties - added additional check to // avoid getBytes returning a byte[] and process if (method.getReturnType().isArray() && !method.getReturnType().isPrimitive() && !method.getReturnType().equals(String[].class) && !method.getReturnType().equals(byte[].class)) { System.out.println(method.getReturnType()); // Type check for primitive arrays (would fail typecasting // in case of int[], char[] etc) if (method.invoke(object) instanceof Object[]) { Object[] objectArray = (Object[]) method.invoke(object); if (objectArray != null) { for (Object obj : (Object[]) objectArray) { // Recursively revisit with the current property trimReflective(obj); } } } } // If a String array if (method.getReturnType().equals(String[].class)) { String[] propertyArray = (String[]) method.invoke(object); if (propertyArray != null) { Method setter = c.getMethod("set" + name.substring(3), new Class<?>[] { String[].class }); if (setter != null) { String[] modifiedArray = new String[propertyArray.length]; for (int i = 0; i < propertyArray.length; i++) if (propertyArray[i] != null) modifiedArray[i] = propertyArray[i].trim(); // Explicit wrapping setter.invoke(object, new Object[] { modifiedArray }); } } } // Collections start if (Collection.class.isAssignableFrom(method.getReturnType())) { Collection collectionProperty = (Collection) method .invoke(object); if (collectionProperty != null) { for (int index = 0; index < collectionProperty.size(); index++) { if (collectionProperty.toArray()[index] instanceof String) { String element = (String) collectionProperty .toArray()[index]; if (element != null) { // Check if List was created with // Arrays.asList (non-resizable Array) if (collectionProperty instanceof List) { ((List) collectionProperty).set(index, element.trim()); } else { collectionProperty.remove(element); collectionProperty.add(element.trim()); } } } else { // Recursively revisit with the current property trimReflective(collectionProperty.toArray()[index]); } } } } // Separate placement for Map with special conditions to process // keys and values if (method.getReturnType().equals(Map.class)) { Map mapProperty = (Map) method.invoke(object); if (mapProperty != null) { // Keys for (int index = 0; index < mapProperty.keySet().size(); index++) { if (mapProperty.keySet().toArray()[index] instanceof String) { String element = (String) mapProperty.keySet() .toArray()[index]; if (element != null) { mapProperty.put(element.trim(), mapProperty.get(element)); mapProperty.remove(element); } } else { // Recursively revisit with the current property trimReflective(mapProperty.get(index)); } } // Values for (Map.Entry entry : (Set<Map.Entry>) mapProperty .entrySet()) { if (entry.getValue() instanceof String) { String element = (String) entry.getValue(); if (element != null) { entry.setValue(element.trim()); } } else { // Recursively revisit with the current property trimReflective(entry.getValue()); } } } } else {// Catch a custom data type as property and send through // recursion Object property = (Object) method.invoke(object); if (property != null) { trimReflective(property); } } } } catch (Exception e) { throw new Exception("Strings cannot be trimmed because: ", e); } return object; } 属性
      2. 属性作为自定义数据类型,而后者又具有String属性
      3. 属性作为自定义数据类型,而自定义数据类型又具有属性作为自定义数据类型,而自定义数据类型又具有String属性
      4. String自定义数据类型
      5. List Set
      6. Strings自定义数据类型
      7. Array Array
      8. Strings Map和自定义数据类型
      9. 对象图:

        enter image description here

        测试对象代码段:

        String

        结果:

        public static Music buildObj() {
                Song song1 = new Song();
                Song song2 = new Song();
                Song song3 = new Song();
        
            Artist artist1 = new Artist();
            Artist artist2 = new Artist();
        
            song1.setGenre("ROCK       ");
            song1.setSonnet("X    ");
            song1.setNotes("Y    ");
            song1.setCompostions(Arrays.asList(new String[] { "SOME X DATA  ",
                    "SOME OTHER DATA X ", "SOME MORE DATA X    ", " " }));
        
            Set<String> instruments = new HashSet<String>();
            instruments.add("         GUITAR    ");
            instruments.add("         SITAR    ");
            instruments.add("         DRUMS    ");
            instruments.add("         BASS    ");
        
            song1.setInstruments(instruments);
        
            song2.setGenre("METAL       ");
            song2.setSonnet("A    ");
            song2.setNotes("B    ");
            song2.setCompostions(Arrays.asList(new String[] { "SOME Y DATA  ",
                    "          SOME OTHER DATA Y ",
                    "           SOME MORE DATA Y    ", " " }));
        
            song3.setGenre("POP       ");
            song3.setSonnet("DONT    ");
            song3.setNotes("KNOW     ");
            song3.setCompostions(Arrays.asList(new String[] { "SOME Z DATA  ",
                    "               SOME OTHER DATA Z ",
                    "          SOME MORE DATA Z   ", " " }));
        
            artist1.setSongList(Arrays.asList(new Song[] { song1, song3 }));
        
            artist2.setSongList(Arrays.asList(new Song[] { song1, song2, song3 }));
            Map<String, Person> artistMap = new HashMap<String, Person>();
            Person tutor1 = new Person();
            tutor1.setName("JOHN JACKSON DOE       ");
            artistMap.put("          Name                 ", tutor1);
        
            Person coach1 = new Person();
            coach1.setName("CARTER   ");
            artistMap.put("Coach      ", coach1);
            artist2.setTutor(artistMap);
        
            music.setSongs(Arrays.asList(new Song[] { song1, song2, song3 }));
            music.setArtists(Arrays.asList(new Artist[] { artist1, artist2 }));
        
            music.setLanguages(new String[] { "    ENGLISH    ", "FRENCH    ",
                    "HINDI    " });
            Person singer1 = new Person();
            singer1.setName("DAVID      ");
        
            Person singer2 = new Person();
            singer2.setName("JACOB      ");
            music.setSingers(new Person[] { singer1, singer2 });
        
            Human man = new Human();
            Person p = new Person();
            p.setName("   JACK'S RAGING BULL   ");
            SomeGuy m = new SomeGuy();
            m.setPerson(p);
            man.setMan(m);
        
            music.setHuman(man);
        
            return music;
        }
        

        上述#######BEFORE####### >>[>>DAVID ---<<, >>JACOB ---<<]---[ ENGLISH , FRENCH , HINDI ]---[>>ROCK ---X ---Y ---[SOME X DATA , SOME OTHER DATA X , SOME MORE DATA X , ]---[ SITAR , GUITAR , BASS , DRUMS ]<<, >>METAL ---A ---B ---[SOME Y DATA , SOME OTHER DATA Y , SOME MORE DATA Y , ]---<<, >>POP ---DONT ---KNOW ---[SOME Z DATA , SOME OTHER DATA Z , SOME MORE DATA Z , ]---<<]---[>>---[>>ROCK ---X ---Y ---[SOME X DATA , SOME OTHER DATA X , SOME MORE DATA X , ]---[ SITAR , GUITAR , BASS , DRUMS ]<<, >>POP ---DONT ---KNOW ---[SOME Z DATA , SOME OTHER DATA Z , SOME MORE DATA Z , ]---<<]<<, >>{Coach =>>CARTER ---<<, Name =>>JOHN JACKSON DOE ---<<}---[>>ROCK ---X ---Y ---[SOME X DATA , SOME OTHER DATA X , SOME MORE DATA X , ]---[ SITAR , GUITAR , BASS , DRUMS ]<<, >>METAL ---A ---B ---[SOME Y DATA , SOME OTHER DATA Y , SOME MORE DATA Y , ]---<<, >>POP ---DONT ---KNOW ---[SOME Z DATA , SOME OTHER DATA Z , SOME MORE DATA Z , ]---<<]<<]---=> JACK'S RAGING BULL <=<< Number of spaces : 644 #######AFTER####### >>[>>DAVID---<<, >>JACOB---<<]---[ENGLISH, FRENCH, HINDI]---[>>ROCK---X---Y---[SOME X DATA, SOME OTHER DATA X, SOME MORE DATA X, ]---[GUITAR, SITAR, DRUMS, BASS]<<, >>METAL---A---B---[SOME Y DATA, SOME OTHER DATA Y, SOME MORE DATA Y, ]---<<, >>POP---DONT---KNOW---[SOME Z DATA, SOME OTHER DATA Z, SOME MORE DATA Z, ]---<<]---[>>---[>>ROCK---X---Y---[SOME X DATA, SOME OTHER DATA X, SOME MORE DATA X, ]---[GUITAR, SITAR, DRUMS, BASS]<<, >>POP---DONT---KNOW---[SOME Z DATA, SOME OTHER DATA Z, SOME MORE DATA Z, ]---<<]<<, >>{Name=>>JOHN JACKSON DOE---<<, Coach=>>CARTER---<<}---[>>ROCK---X---Y---[SOME X DATA, SOME OTHER DATA X, SOME MORE DATA X, ]---[GUITAR, SITAR, DRUMS, BASS]<<, >>METAL---A---B---[SOME Y DATA, SOME OTHER DATA Y, SOME MORE DATA Y, ]---<<, >>POP---DONT---KNOW---[SOME Z DATA, SOME OTHER DATA Z, SOME MORE DATA Z, ]---<<]<<]---=>JACK'S RAGING BULL<=<< Number of spaces : 111 输出中的空格数计数非零,因为我没有努力覆盖任何集合的trimmed toString,{ {1}})或List。我想要的代码有一些改进,但对于你的情况,解决方案应该工作得很好。

        限制(进一步改进)

        1. 无法处理属性的无纪律语法(无效的getter / setters)
        2. 无法处理链式集合:例如,Set - 因为对纪律严明的getter / setter惯例的独家支持
        3. 没有Map集合库支持

答案 2 :(得分:1)

我提出了一种使用Reflection API修剪String值的简单方法。

public Object trimStringValues(Object model){
  for(Field field : model.getClass().getDeclaredFields()){
        try{
        field.setAccessible(true);
        Object value = field.get(model);
        String fieldName = field.getName();
         if(value != null){
            if(value instanceof String){
                String trimmed = (String) value;
                field.set(model, trimmed.trim());
                }

            }
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                }
}

我还没有碰到这个问题。我知道它是一个旧线程,但它可能有助于somone whos正在寻找简单的东西。

答案 3 :(得分:1)

以@SwissArmyKnife为基础,我将其简单的String修剪函数转换为具有默认方法的接口。因此,任何您想使用object.trim()的对象,都只需添加“ Trimmable实现”。

简单的字符串修剪接口:Trimmable.class

/**
 * Utility interface that trims all String fields of the implementing class.
 */
public interface Trimmable {

    /**
     * Trim all Strings
     */
    default void trim(){
        for (Field field : this.getClass().getDeclaredFields()) {
            try {
                field.setAccessible(true);
                Object value = field.get(this);
                if (value != null){
                    if (value instanceof String){
                        String trimmed = (String) value;
                        field.set(this, trimmed.trim());
                    }
                }
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
    }
}

我们希望可修剪的对象:Person.class(实现Trimmable接口)

public class Person implements Trimmable {
     private String firstName;
     private String lastName;
     private int age;

     // getters/setters omitted
}

现在您可以使用person.trim()

Person person = new Person();
person.setFirstName("    John   ");
person.setLastName("  Doe");
person.setAge(30);
person.trim();