使用SnakeYAML保持标签顺序

Dan*_*iel 6 json yaml snakeyaml

我正在尝试将yaml文件翻译为json,但是翻译重新排序标签... Ex,YAML source:

zzzz:
  b: 456
  a: dfff
aa:
  s10: "dddz"
  s3: eeee
bbb:
 - b1
 - a2
Run Code Online (Sandbox Code Playgroud)

snakeYAML产生:

{
  "aa": {
    "s3": "eeee",
    "s10":"dddz"
  },
  "bbb":[
    "b1",
    "a2"
  ],
  "zzzz": {
    "a": "dfff",
    "b":456
  }
}
Run Code Online (Sandbox Code Playgroud)

bek*_*kce 4

在代码中创建以下类,这是 SnakeYAML 源的一个调整版本,它使用LinkedHashMapLinkedHashSet保持插入顺序,而不是TreeMapTreeSet自动排序它们。

    import java.beans.FeatureDescriptor;
    import java.beans.IntrospectionException;
    import java.beans.Introspector;
    import java.beans.PropertyDescriptor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    import java.util.*;
    
    import org.yaml.snakeyaml.error.YAMLException;
    import org.yaml.snakeyaml.introspector.*;
    import org.yaml.snakeyaml.util.PlatformFeatureDetector;
    
    public class CustomPropertyUtils extends PropertyUtils {
    
        private final Map<Class<?>, Map<String, Property>> propertiesCache = new HashMap<Class<?>, Map<String, Property>>();
        private final Map<Class<?>, Set<Property>> readableProperties = new HashMap<Class<?>, Set<Property>>();
        private BeanAccess beanAccess = BeanAccess.DEFAULT;
        private boolean allowReadOnlyProperties = false;
        private boolean skipMissingProperties = false;
    
        private PlatformFeatureDetector platformFeatureDetector;
    
        public CustomPropertyUtils() {
            this(new PlatformFeatureDetector());
        }
    
        CustomPropertyUtils(PlatformFeatureDetector platformFeatureDetector) {
            this.platformFeatureDetector = platformFeatureDetector;
    
            /*
             * Android lacks much of java.beans (including the Introspector class, used here), because java.beans classes tend to rely on java.awt, which isn't
             * supported in the Android SDK. That means we have to fall back on FIELD access only when SnakeYAML is running on the Android Runtime.
             */
            if (platformFeatureDetector.isRunningOnAndroid()) {
                beanAccess = BeanAccess.FIELD;
            }
        }
    
        protected Map<String, Property> getPropertiesMap(Class<?> type, BeanAccess bAccess) {
            if (propertiesCache.containsKey(type)) {
                return propertiesCache.get(type);
            }
    
            Map<String, Property> properties = new LinkedHashMap<String, Property>();
            boolean inaccessableFieldsExist = false;
            switch (bAccess) {
                case FIELD:
                    for (Class<?> c = type; c != null; c = c.getSuperclass()) {
                        for (Field field : c.getDeclaredFields()) {
                            int modifiers = field.getModifiers();
                            if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)
                                    && !properties.containsKey(field.getName())) {
                                properties.put(field.getName(), new FieldProperty(field));
                            }
                        }
                    }
                    break;
                default:
                    // add JavaBean properties
                    try {
                        for (PropertyDescriptor property : Introspector.getBeanInfo(type)
                                .getPropertyDescriptors()) {
                            Method readMethod = property.getReadMethod();
                            if ((readMethod == null || !readMethod.getName().equals("getClass"))
                                    && !isTransient(property)) {
                                properties.put(property.getName(), new MethodProperty(property));
                            }
                        }
                    } catch (IntrospectionException e) {
                        throw new YAMLException(e);
                    }
    
                    // add public fields
                    for (Class<?> c = type; c != null; c = c.getSuperclass()) {
                        for (Field field : c.getDeclaredFields()) {
                            int modifiers = field.getModifiers();
                            if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)) {
                                if (Modifier.isPublic(modifiers)) {
                                    properties.put(field.getName(), new FieldProperty(field));
                                } else {
                                    inaccessableFieldsExist = true;
                                }
                            }
                        }
                    }
                    break;
            }
            if (properties.isEmpty() && inaccessableFieldsExist) {
                throw new YAMLException("No JavaBean properties found in " + type.getName());
            }
            System.out.println(properties);
            propertiesCache.put(type, properties);
            return properties;
        }
    
        private static final String TRANSIENT = "transient";
    
        private boolean isTransient(FeatureDescriptor fd) {
            return Boolean.TRUE.equals(fd.getValue(TRANSIENT));
        }
    
        public Set<Property> getProperties(Class<? extends Object> type) {
            return getProperties(type, beanAccess);
        }
    
        public Set<Property> getProperties(Class<? extends Object> type, BeanAccess bAccess) {
            if (readableProperties.containsKey(type)) {
                return readableProperties.get(type);
            }
            Set<Property> properties = createPropertySet(type, bAccess);
            readableProperties.put(type, properties);
            return properties;
        }
    
        protected Set<Property> createPropertySet(Class<? extends Object> type, BeanAccess bAccess) {
            Set<Property> properties = new LinkedHashSet<>();
            Collection<Property> props = getPropertiesMap(type, bAccess).values();
            for (Property property : props) {
                if (property.isReadable() && (allowReadOnlyProperties || property.isWritable())) {
                    properties.add(property);
                }
            }
            return properties;
        }
    
        public Property getProperty(Class<? extends Object> type, String name) {
            return getProperty(type, name, beanAccess);
        }
    
        public Property getProperty(Class<? extends Object> type, String name, BeanAccess bAccess) {
            Map<String, Property> properties = getPropertiesMap(type, bAccess);
            Property property = properties.get(name);
            if (property == null && skipMissingProperties) {
                property = new MissingProperty(name);
            }
            if (property == null) {
                throw new YAMLException(
                        "Unable to find property '" + name + "' on class: " + type.getName());
            }
            return property;
        }
    
        public void setBeanAccess(BeanAccess beanAccess) {
            if (platformFeatureDetector.isRunningOnAndroid() && beanAccess != BeanAccess.FIELD) {
                throw new IllegalArgumentException(
                        "JVM is Android - only BeanAccess.FIELD is available");
            }
    
            if (this.beanAccess != beanAccess) {
                this.beanAccess = beanAccess;
                propertiesCache.clear();
                readableProperties.clear();
            }
        }
    
        public void setAllowReadOnlyProperties(boolean allowReadOnlyProperties) {
            if (this.allowReadOnlyProperties != allowReadOnlyProperties) {
                this.allowReadOnlyProperties = allowReadOnlyProperties;
                readableProperties.clear();
            }
        }
    
        public boolean isAllowReadOnlyProperties() {
            return allowReadOnlyProperties;
        }
    
        /**
         * Skip properties that are missing during deserialization of YAML to a Java
         * object. The default is false.
         *
         * @param skipMissingProperties
         *            true if missing properties should be skipped, false otherwise.
         */
        public void setSkipMissingProperties(boolean skipMissingProperties) {
            if (this.skipMissingProperties != skipMissingProperties) {
                this.skipMissingProperties = skipMissingProperties;
                readableProperties.clear();
            }
        }
    
        public boolean isSkipMissingProperties() {
            return skipMissingProperties;
        }
    }
Run Code Online (Sandbox Code Playgroud)

然后,Yaml像这样创建您的实例:

        DumperOptions options = new DumperOptions();
        CustomPropertyUtils customPropertyUtils = new CustomPropertyUtils();
        Representer customRepresenter = new Representer();
        customRepresenter.setPropertyUtils(customPropertyUtils);
        Yaml yaml = new Yaml(customRepresenter, options);
Run Code Online (Sandbox Code Playgroud)

利润!