4

I am creating a helper class in parsing XML elements, so the developer do not need to know the exact name and capitalization of the XML fields.

private static class TagNames{
      public static String RESOURCE_ID = "ResourceId";
      public static String RESOURCE_NAME = "ResourceName";
      public static String RESOURCE_PRICE = "ResourcePrice";
}

This makes it easier to do things like:

someXMLParser.getValueByTagName(TagNames.RESOURCE_ID);

My question is this. If I want to iterate over all the fields declared in class TagNames, how do I do that? Pseudocode:

For tag in TagNames:
   someXMLParser.getValueByTagName(tag)

I know I will probably have to restructure all of this. But I can't figure out a way to make the names easily accessible as well as iterable, without any duplication.

Any suggestions?

5 Answers 5

2

You're literally asking for a solution based on reflection, but I think a Java Enum may be a better choice in this case. Building on Frederick's example:

public class EnumTest {
    public enum Tags {
        RESOURCE_ID("ResourceId"), 
        REOURCE_NAME("ResourceName"), 
        RESOURCE_PRICE("ResourcePrice");

        private final String tagName;
        Tags(String tagName) {
            this.tagName = tagName;
        }

        public String getTagName() {
            return tagName;
        }
    }

    public static void main(String[] args) {
        for(Tags tag : Tags.values()) {
            System.out.println("const:" + tag.name() 
                    + " tagName:" + tag.getTagName());
        }
        // API user might do e.g.:
        // document.getValueForTag(Tags.REOURCE_NAME);
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

@maerics - so similar to your answer!
thanks for the mention, I do think your way is better though :)
2

Although I agree that you should probably use enums or ResourceBundles, here's a solution to your actual question. A method that generates a Map name -> value from all public constants in a given class (the only thing that's missing should be try / catch or throws)

public static Map<String, Object> getConstantValues(Class<?> clazz){

    Map<String, Object> constantValues = new LinkedHashMap<String, Object>();
    for(Field field : clazz.getDeclaredFields()){
        int modifiers = field.getModifiers();
        if(Modifiers.isPublic(mod)
            && Modifiers.isStatic(mod) && Modifiers.isFinal(mod)){
            constantValues.put(field.getName(), field.get(null));
        }
    }
    return constantValues;
}

2 Comments

public static constant? :-) Is that some C#! But you're right, I suppose reflection is the most literal answer to his question.
Oh lord there was another awful bug. That's what you get for answering SO questions before breakfast :-)
1

You may want to consider using a ResourceBundle instead of a class to store the tag names. May require a little bit of reworking of your code but it will be easier to produce a list of tags compared to what you are doing now, and adding a new tag won't require much work other then adding a line to the properties file.

Comments

0

You can do this quite easily using enum and an accompanying array:

public class Main {

    public enum TagName { RESOURCE_ID, REOURCE_NAME, RESOURCE_PRICE }

    private static String[] tags = {"ResourceID", "ResourceName", "ResourcePrice"};

    public static String getValueByTagName(TagName tag) {

    return tags[tag.ordinal()];
    }

    public static void main(String[] args) {

    System.out.println("Calling by getValueByTagName:");
    System.out.println(getValueByTagName(TagName.RESOURCE_ID));

    System.out.println("Calling TagName.values() for loop:");

    for (TagName t : TagName.values()) {

        System.out.println(getValueByTagName(t));
    }
    }
}

1 Comment

Good, but I would suggest storing the tag names in the enum itself, so you don't have to worry about lining up the enum constant ordinals with the indexing into the tagName array.
0

Using an enum is a good fit, especially if you use a custom constructor and the built in "values" method:

public class Main {
  public static enum TagName {
    RESOURCE_ID("ResourceId"),
    RESOURCE_NAME("ResourceName"),
    RESOURCE_PRICE("ResourcePrice"),
    ;
    private String s;
    private TagName(String s) { this.s = s; }
    public String toString() { return this.s; }
    public static String[] strings() {
      List<String> ss = new ArrayList<String>();
      for (TagName tagName : TagName.values()) {
        ss.add(tagName.toString());
      }
      return ss.toArray(new String[ss.size()]);
    }
  }
  public static void main(String[] args) {
    // Use TagName.values() for the enums, or for strings...
    for (String s : TagName.strings()) {
      System.out.println(s);
    }
  }
}

This way you can simply add new tags and they'll automatically get picked up by the "strings" method; for extra performance you could compute that string array just once, statically, since you can't change the set of enums dynamically. You could get even fancier by auto-generating the tag strings from their constant values, if they are really normalized...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.