2016-10-02 82 views
3

所以有這個類org.apache.commons.lang3.text稱爲StrSubstitutor它可以在一張地圖這樣的:爪哇 - 從字符串使用模板提取鍵值對

Map<String, String> params = new HashMap<String, String>(); 
params.put("name","Vivek"); 
params.put("id","900"); 
params.put("somethingelse","blah"); 

和一個模板字符串是這樣的:

<data> 
     <id>${id}</id> 
     <name>${name}</name> 
     <something>${somethingelse}</something> 
    </data> 

產生一個輸出字符串是這樣的:

<data> 
     <id>900</id> 
     <name>Vivek</name> 
     <something>blah</something> 
    </data> 

我要的是相反的。有沒有一種方法可以讓輸出字符串和模板使用模板變量作爲鍵和字符串中相應的值作爲值來填充地圖?

PS - 我將使用的字符串不一定總是XML。這僅僅是一個例子。

編輯:我覺得有些困惑,變量名和標籤名稱是一樣的。標籤只是說明性的,與問題無關。這是我關心的$ {}中的變量。我添加了另一個標籤來顯示我的意思。

+0

您是否知道可能的標籤列表(如「id」,「name」等),或者您需要解決未知標籤的問題? – c0der

+0

@ c0der標籤無關緊要。如果你的意思是變量 - 是的,如果它是未知的,它會更好。基本上我想解析出任何包含在$ {} –

+0

中的任何東西如果你想要一個通用和高效的解決方案,你將不得不解析這個結構。這些簡單的例子可以通過手寫解析器來解析。如果這個例子是一個更大的規範的一部分,你可能更好考慮一個解析器生成器。 – CoronA

回答

0

你可以用一個模式來格式字符串轉換成正則表達式,然後用這個表達式進步的輸入字符串,下面的示例類:

public final class FormatReader { 

    private final Pattern formatPattern; 
    private final List<String> names; 

    private FormatReader(
      final Pattern formatPattern, 
      final List<String> names) { 
     //// 
     this.formatPattern = formatPattern; 
     this.names = names; 
    } 

    public static FormatReader of(
      final String prefix, 
      final String suffix, 
      final String format) { 
     //// 
     return of(prefix, suffix, format, true); 
    } 

    public static FormatReader of(
      final String prefix, 
      final String suffix, 
      final String format, 
      final boolean allowSurroundingWhitespace) { 
     //// 
     // This method is somewhat ugly... 
     final List<String> names = new ArrayList<>(); 
     final StringBuilder sb = new StringBuilder("(?m)"); 
     boolean skip = allowSurroundingWhitespace; 
     if (skip) 
      sb.append("\\s*"); 
     for (int i = 0, last = 0, prefixLength = prefix.length(), suffixLength = suffix.length();;) { 
      if (i == format.length()) { 
       if (!skip) 
        sb.append(Pattern.quote(format.substring(last))); 
       break; 
      } 
      if (format.startsWith(prefix, i)) { 
       skip = true; 
       sb.append(Pattern.quote(format.substring(last, i))).append("(.+)"); 

       final int off = i + prefixLength; 
       names.add(format.substring(off, i = format.indexOf(suffix, off))); 
       i += suffixLength; 
       continue; 
      } 
      if (Character.isWhitespace(format.charAt(i))) { 
       if (!skip) { 
        skip = true; 
        // Replace '\s*' with '\s+' if at least one whitespace has to be present 
        sb.append(Pattern.quote(format.substring(last, i))).append("\\s*"); 
       } 
      } else if (skip) { 
       last = i; 
       skip = false; 
      } 
      i++; 
     } 
     if (!skip && allowSurroundingWhitespace) 
      sb.append("\\s*"); 
     return new FormatReader(Pattern.compile(sb.toString()), names); 
    } 

    public Map<String, String> toMap(
      final String input) { 
     //// 
     final Matcher m = formatPattern.matcher(input); 
     if (!m.matches()) 
      throw new IllegalArgumentException("Argument does not match format"); 
     final Map<String, String> map = new HashMap<>(); 
     for (int i = 0; i < m.groupCount();) 
      map.put(names.get(i), m.group(++i)); 
     return map; 
    } 

    public static void main(
      final String[] args) { 
     //// 
     final FormatReader r = of("${", "}", "" 
       + " <data>\n" 
       + "  <id>${id}</id>\n" 
       + "  <name>${name}</name>\n" 
       + " </data>"); 
     final String s = "" 
       + "  <data>\n" 
       + "  <id>900</id>   " 
       + " <name>Vivek</name>\n" 
       + " </data> "; 
     // The created pattern (accepts any count of whitespace): 
     //        'id'      'name' 
     // (?m)\s*\Q<data>\E\s*\Q<id>\E(.+)\Q</id>\E\s*\Q<name>\E(.+)\Q</name>\E\s*\Q</data>\E\s* 
     System.out.println(r.toMap(s)); // {name=Vivek, id=900} 
    } 
} 
+0

它在盲目運行時工作。一點解釋會有所幫助。 –

+0

好的,我想我現在明白它的作用。感謝你的回答!我會看看是否有人在我接受你的答案之前提出了一個可以做到這一點的現有庫。 –

+0

好吧,我現在發現代碼對於有一個空白字符很敏感。這是有道理的,因爲你不能將字符串與模板匹配。遺憾的是,這對我的問題來說太有限制了。 –

0

這裏是另一種選擇:

import java.util.HashMap; 
import java.util.Map; 

public class Test{ 

    public static void main(String[] args){ 

     //simulate template. Assuming no more than on param in line 
     String[] template = new String[]{ 
            "<data>", 
            "<id>${id}</id>", 
            "<name>${name}</name>", 
            "<something>${somethingelse}</something>", 
            "</data>" 
            }; 

     String[] output = new String[]{ 
            "<data>", 
            "<id>900</id>", 
            "<name>Vivek</name>", 
            "<somethingelse>blah</somethingelse>", 
            "</data>" 
            }; 

     Map<String, String> params = getParams(template); 

     getValues(params, output); 

     for(String key : params.keySet()) { 
      System.out.println(key +" : " + params.get(key)); 
     } 
    } 

    private static Map<String, String> getParams(String[] template) { 

     Map<String, String> params = new HashMap<String, String>(); 

     for (String line : template) { 

      //get location of 3 chars ${} 
      int index$ = line.indexOf("$"); 
      int indexLeftB = line.indexOf("{"); 
      int indexRightB = line.indexOf("}"); 

      //make sure all ${} are present 
      if((index$ <0) || (indexLeftB <0) || (indexRightB <0)) { 
       continue; 
      } 

      //make sure they are in the right order 
      if(((indexLeftB - index$) !=1) || (indexRightB < indexLeftB)) { 
       continue; 
      } 

      //get param 
      String param = getParamFromLine(line, indexLeftB+1 , indexRightB); 

      if(param != null) { 

       params.put(param,null); 
      } 
     } 

     return params; 
    } 

    private static void getValues(Map<String, String> params, String[] output) { 

     //iterate over map 
     for(String param : params.keySet()) { 

      String tag = "<"+param+">"; //like <name> 
      String closeTag = "</"+param+">"; //like <name> 

      //iterate over output 
      for(String line : output) { 

       line = line.trim(); //remove all whitespace 
       //look for first occurrence of patternToSearch 
       int index1 = line.indexOf(tag, 0); 
       int index2 = line.indexOf(closeTag, index1); 

       //make sure there are 2 occurrences in 
       if((index1 < 0) || (index2 < 0)) { 
        continue; 
       } 

       String value = getParamFromLine(line, index1+ tag.length(), index2); 
       if(value != null) { 

        params.put(param, value); 
       } 
      } 
     } 
    } 

    private static String getParamFromLine(String line, int indexLeftB, int indexRightB) { 

     String param = line.substring(indexLeftB, indexRightB); 

     return (param.trim().length() == 0) ? null : param.trim(); 
    } 
} 
+0

反饋將不勝感激 – c0der