RussellSpitzer / snakeyaml

Automatically exported from code.google.com/p/snakeyaml
Apache License 2.0
0 stars 1 forks source link

Incorrect architecture #169

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
The class Constructor.ConstructMapping marked as a protected
The method constructJavaBean2ndStep marked as a protected also
With the private typeDefinitions overwriting of constructJavaBean2ndStep is 
useless. (and this is the only place where typeDefinitions used)

What is the expected output? What do you see instead?
- mark constructJavaBean2ndStep as a private
- or add a typeDefinitions getter
- or mark typeDefinitions as a protected
- or decompose constructJavaBean2ndStep

What version of SnakeYAML are you using? On what Java version?
SnakeYAML 1.11

Original issue reported on code.google.com by alexey....@gmail.com on 13 Feb 2013 at 3:20

GoogleCodeExporter commented 9 years ago
Check the tests to see when constructJavaBean2ndStep is used.
If you have a proposal, feel free to change the code and provide a test to see 
what you wish to achieve with your proposal.

Original comment by py4fun@gmail.com on 14 Feb 2013 at 12:13

GoogleCodeExporter commented 9 years ago
I'm sorry. I was a bit tired after fighting with snakeyaml. Suspect that my 
ticket is not clear enough. Context is:

I use Scala and I have custom serialization and deserialization code. I achieve 
my target, but custom deserialization part is a bit junk. I read snakeyaml code 
a lot and understand when and how constructJavaBean2ndStep is used.

The problem is that end user (I am) unable to get access to typeDefinitions and 
correct constructJavaBean2ndStep for custom case. I am unable to fix 
constructJavaBean2ndStep based on typeDefinitions so I re implement 
AbstractConstruct. I suspect that it is not so good. It is possible to add
protected Map<Class<? extends Object>, TypeDescription> getTypeDefinitions()?
or
mark 
private final Map<Class<? extends Object>, TypeDescription> typeDefinitions;
as protected.

IMHO This is absolutely minor changes. Tests is not needed.

King regards,
Alexey

Original comment by alexey....@gmail.com on 14 Feb 2013 at 7:35

GoogleCodeExporter commented 9 years ago
The fact that only you and only now need such a change might mean that there is 
another way to do the same thing.
The test would help to see your use case.
It is not a problem to change the code. We would like just to understand why 
the change is required.
Fee free to provide the Scala code. 

Original comment by py4fun@gmail.com on 15 Feb 2013 at 7:42

GoogleCodeExporter commented 9 years ago
Here is a code. Please note that TypeSchemaRepresenter at the bottom - only few 
lines of code, beautiful. I want to reuse an exists snakeyaml code as much as 
possible for the deserialization. Right now I must reimplement 
InterfaceConstruct with AbstractConstruct instead of extend ConstructMapping, 
sad. TypeSchema.Interface and TypeSchema.Entity are not follow bean naming 
conversion for some reasons. I read a documentation and search across an 
Internet but not find an any strait solution to recreate custom classes without 
bean properties.

  object YAML extends Payload.YAMLProcessor[TypeSchema.Interface] {
    /** Convert JSON to the object */
    def from(data: String): TypeSchema.Interface = {
      val yaml = new Yaml(new TypeSchemaConstructor)
      yaml.load(data).asInstanceOf[TypeSchema.Interface]
    }
    /** Convert the object to JSON */
    def to(value: TypeSchema.Interface): String = {
      val options = new DumperOptions()
      options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK)
      val yaml = new Yaml(new TypeSchemaRepresenter, new DumperOptions())
      yaml.dump(value)
    }

    class TypeSchemaConstructor extends Constructor(classOf[Interface]) {
      val interfaceTag = new Tag(classOf[Interface])
      val entityTag = new Tag(classOf[Entity])
      this.yamlConstructors.put(interfaceTag, new InterfaceConstruct())
      this.yamlConstructors.put(entityTag, new EntityConstruct())

      def safeConstruct[T: Manifest](tuple: NodeTuple)(implicit owner: String): Option[T] = constructObject(tuple.getValueNode()) match {
        case value: T => Option(value)
        case unknown => throw new YAMLException("Unexpected %s key '%s' with type %s".
          format(owner, constructObject(tuple.getKeyNode), tuple.getValueNode().getTag()))
      }
      class InterfaceConstruct extends AbstractConstruct {
        implicit val owner = "TypeSchema.Interface"

        def construct(node: Node): AnyRef = node match {
          case node: MappingNode =>
            var id: Option[String] = None
            var name: Option[String] = None
            var description: Option[String] = None
            var entities: scala.collection.mutable.Buffer[TypeSchema.Entity] = new scala.collection.mutable.ArrayBuffer
            for (value <- node.getValue())
              constructObject(value.getKeyNode()) match {
                case "id" => id = safeConstruct[String](value)
                case "name" => name = safeConstruct[String](value)
                case "description" => description = safeConstruct[String](value)
                case "entities" =>
                  val node = value.getValueNode() match {
                    case seq: SequenceNode =>
                      entities = for (value <- seq.getValue()) yield {
                        value.setTag(entityTag)
                        constructObject(value).asInstanceOf[TypeSchema.Entity]
                      }
                    case unknown => throw new YAMLException("Unexpected TypeSchema.Interface 'entities' type " + unknown.getClass())
                  }
                case other => log.warn(s"unknown TypeSchema.Interface key: $other")
              }
            val schema = for {
              id <- id
              name <- name
              description <- description
            } yield new TypeSchema(UUID.fromString(id), name, description, immutable.HashSet(entities.filter(_ != null): _*))
            schema getOrElse {
              log.error(s"Unable to load TypeSchema.Interface id:$id, name:$name, description:$description, entities:" + entities.mkString)
              null
            }
          case unknown =>
            throw new YAMLException("Unexpected TypeSchema.Interface node type " + unknown.getTag())
        }
      }
      class EntityConstruct extends AbstractConstruct {
        implicit val owner = "TypeSchema.Entity"

        def construct(node: Node): AnyRef = node match {
          case node: MappingNode =>
            var `type`: Option[String] = None
            var alias: Option[String] = None
            var availability: Option[Boolean] = None
            var description: Option[String] = None
            for (value <- node.getValue())
              constructObject(value.getKeyNode()) match {
                case "type" => `type` = safeConstruct[String](value)
                case "alias" => alias = safeConstruct[String](value)
                case "availability" => availability = safeConstruct[java.lang.Boolean](value).map(Boolean.box(_))
                case "description" => description = safeConstruct[String](value)
                case other => log.warn(s"unknown TypeSchema.Entity key: $other")
              }
            val entity = for {
              `type` <- `type`
              alias <- alias
              availability <- availability
              description <- description
            } yield TypeSchema.Entity(Symbol(`type`), alias, availability, description)
            entity.getOrElse {
              log.error(s"Unable to load TypeSchema.Entity type:%s, alias:$alias, availability:$availability, description:$description".format(`type`))
              null
            }
          case unknown =>
            throw new YAMLException("Unexpected TypeSchema.Entity node type " + unknown.getTag())
        }
      }
    }

    class TypeSchemaRepresenter extends Representer {
      multiRepresenters.put(classOf[Interface], new InterfaceRepresent)
      multiRepresenters.put(classOf[Entity], new EntityRepresent)

      class InterfaceRepresent extends Represent {
        override def representData(data: AnyRef): Node = {
          val schema = data.asInstanceOf[TypeSchema]
          val map = new java.util.HashMap[String, AnyRef]()
          map.put("id", schema.id.toString())
          map.put("name", schema.name)
          map.put("description", schema.description)
          map.put("entities", seqAsJavaList(schema.entities.toSeq))
          representMapping(Tag.MAP, map, null)
        }
      }
      class EntityRepresent extends Represent {
        override def representData(data: AnyRef): Node = {
          val entity = data.asInstanceOf[Entity]
          val map = new java.util.HashMap[String, AnyRef]()
          map.put("type", entity.typeSymbol.name)
          map.put("alias", entity.alias)
          map.put("availability", Boolean.box(entity.availability))
          map.put("description", entity.description)
          representMapping(Tag.MAP, map, null)
        }
      }
    }
  }
}

Example of serialized data:

id: ec2cba2f-68b9-47fb-8616-f8d0cc926ed6
description: blablabla
name: lalala
entities:
- {alias: '*thereIsNoData_text', description: tadadada, type: String,
  availability: true}

Original comment by alexey....@gmail.com on 15 Feb 2013 at 9:00

GoogleCodeExporter commented 9 years ago
are you working with some open source project? Where I could look in 
TypeSchema.Interface ?

-alex

Original comment by alexande...@gmail.com on 18 Feb 2013 at 6:55

GoogleCodeExporter commented 9 years ago
Project will be open source. Right now it is unpublished.

Please note: "TypeSchema.Interface and TypeSchema.Entity are not follow bean 
naming conversion for some reasons." I don't want to add @beanproperty 
annotation to trait or flood it with getters and setters.

Could you please explain, how concrete realization could help for this case. 
The problem is: end user unable to reuse library code for deserialize custom 
nested classes. And there is neither a documentation nor examples of such type 
deserialization. Rewriting library code with copy'n'paste or designing 
workaround instead just extend exists class a bit inappropriate. Please correct 
me.

  /**
   * The type schema entity. It is a tuple typeSymbol -> context information
   */
  case class Entity(
    /** typeSymbol */
    val typeSymbol: Symbol,
    /** typeSymbol alias */
    val alias: String,
    /** Availability flag for user (some types may exists, but not involved in new element template creation) */
    val availability: Boolean,
    /** The enumeration description */
    val description: String) {
    lazy val view: String = if (alias.startsWith("*"))
      Resources.messages.get(alias.substring(1)).getOrElse {
        val result = alias.substring(1)
        val trimmed = if (result.endsWith("_text"))
          result.substring(0, result.length - 5)
        else
          result
        trimmed(0).toString.toUpperCase + trimmed.substring(1)
      }
    else if (alias.isEmpty())
      Resources.messages.get(typeSymbol.name.toLowerCase() + "_text").getOrElse(typeSymbol.name)
    else
      alias
  }
  /**
   * The base TypeSchema interface
   */
  trait Interface {
    /** The type schema id */
    val id: UUID
    /** The type schema name */
    val name: String
    /** The type schema description */
    val description: String
    /** Type schema entities */
    val entities: immutable.HashSet[Entity]

    /** The copy constructor */
    def copy(id: UUID = this.id,
      name: String = this.name,
      description: String = this.description,
      entities: immutable.HashSet[TypeSchema.Entity] = this.entities): this.type
  }

Original comment by alexey....@gmail.com on 18 Feb 2013 at 7:36

GoogleCodeExporter commented 9 years ago
I am just curious :)

Reason I'd like to see "bean" is that in my clone I've tried (hopefully I will 
continue) to make "bean" (de)serialization easier by using customized 
TypeDescriptions.

We will make typeDefinitions protected.

Original comment by alexande...@gmail.com on 18 Feb 2013 at 8:33

GoogleCodeExporter commented 9 years ago
Done. Please try the latest 1.12-SNAPSHOT

http://code.google.com/p/snakeyaml/source/detail?r=31466f3a58a266f49fc13e01375d6
2263aefaf3b

Original comment by py4fun@gmail.com on 18 Feb 2013 at 5:53

GoogleCodeExporter commented 9 years ago

Original comment by py4fun@gmail.com on 2 Apr 2013 at 5:01