Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom converter #25

Closed
ghost opened this issue Jun 19, 2020 · 7 comments
Closed

Custom converter #25

ghost opened this issue Jun 19, 2020 · 7 comments
Assignees
Labels
enhancement New feature or request

Comments

@ghost
Copy link

ghost commented Jun 19, 2020

Is your feature request related to a problem? Please describe.
I would like to write a custom converter, as I for example need to explicitly define the namespace for each node like ns:node and would be cool to have a converter to prefix nodes with a namespace.

Describe the solution you'd like
Having a way to pass the builderOptions a custom converter, to achieve something like the following when treating with a repetitive namespace within childs:

obj = {
    'root': {
        'ns:': { 
            'node1': 'Some text',
            'node2': 1234
        }
    }  
}

would result in:

<root xmlns:ns="some/uri">
    <ns:node1>Some text</ns:node1>
    <ns:node2>1234</ns:node2>
</root>

For it, creating a namespace: converter would solve the repetitive part of writing the namespace each time like so:

obj = {
    'root': {
        'ns:node1': 'Some text',
        'ns:node2': 1234
    }  
}

Describe alternatives you've considered
The alternative unfortunately is being explicit in the definition of each node with the same namespace, which does not look as good and requires to write it every time.

Additional context
N/A

@ghost ghost added the enhancement New feature or request label Jun 19, 2020
@ghost ghost assigned oozcitak Jun 19, 2020
@oozcitak
Copy link
Owner

Take a look at namespace aliases please. Basically you can do:

const { create } = require('xmlbuilder2');

// define a namespace alias
const doc = create({ namespaceAlias: { 
    ns1: 'a-long-namespace-i-dont-want-repeat',
    ns2: 'another-long-namespace' 
} });

// use with @@ notation
doc.ele({
    'root': {
        'node1@@ns1': 'Some text',
        'node2@@ns1': 1234,
        'node3@@ns2': 'test'
    }  
});

console.log(doc.end({ prettyPrint: true })); 
<?xml version="1.0"?>
<root>
    <node1 xmlns="a-long-namespace-i-dont-want-repeat">Some text</node1>
    <node2 xmlns="a-long-namespace-i-dont-want-repeat">1234</node2>
    <node3 xmlns="another-long-namespace">test</node3>
</root>

Would that solve your issue?

@ghost
Copy link
Author

ghost commented Jun 21, 2020

Thanks for the quick response.

Actually, although in theory your example would comply with XML rules to achieve the same result, it would also generate a far more bigger file. Declaring the namespace in the root and declaring nodes with its alias like my example would avoid having to repeat the xmlns attribute in each node. So unfortunately I would have to answer your question negatively. I would very much like to have a way to achieve the result of my example.

oozcitak added a commit that referenced this issue Jun 22, 2020
@oozcitak
Copy link
Owner

oozcitak commented Jun 22, 2020

I totally misunderstood your question. Sorry for that. What you asked for is already possible, like so:

const obj = {
    'root': {
        '@xmlns:ns': 'some/uri',
        'ns:node1': 'Some text',
        'ns:node2': 1234
    }  
}

Please let me know if this answers your question. Otherwise, a custom converter can also be possible.

@ghost
Copy link
Author

ghost commented Jun 22, 2020

No problem, I may need to adjust the way to communicate as well.

I understood that you could represent various child all as an object notation, but as you can see in your example, you are repeating ns twice for node1 and node2, and taking into account a big XML not just two nodes, having a way to refactor it to avoid the namespace repetition in its definition would be very helpful, hence a custom converter to represent a repetitive namespace pattern like I suggested.

@ghost
Copy link
Author

ghost commented Jul 10, 2020

Hey @oozcitak. Any word on how could we help achieve custom converters? I can maybe work on a PR but I wanted to check with you first, perhaps you already started working on it or have a pretty good idea on how to achieve it. Thanks!

@oozcitak
Copy link
Owner

oozcitak commented Jul 10, 2020

Well I started by separating parsers into their own classes. Now I need to refactor them into sharing a common base class. While doing that I also need to move the string parser from oozcitak/dom so that I can modify it. Then I will be able to expose individual converter functions so that they'll be able to affect parser behavior. I haven't yet decided how best to do that. There is still a lot of work ahead. PRs/ideas are always welcome.

@oozcitak
Copy link
Owner

oozcitak commented Aug 4, 2020

I just released v2.3.0 with custom parsers. Following example is from the documentation:

const { create } = require('xmlbuilder2');

const obj = {
  'root': {
    '-ns1:some/uri': { // namespace scope - prefix: ns1, ns: some/uri
      'node1': '',
      'node2': ''
    },
    '-': { // no namespace
      'node3': ''
    }    
  }
};

let prefix;
let ns;
const elementParser = function (parent, namespace, name) {
  if (name.startsWith('-')) {
    let [elePrefix, eleNS] = name.substring(1).split(':');
    if (eleNS === undefined) {
      prefix = undefined;
      ns = undefined;
      return parent;
    }
    else {
      prefix = elePrefix;
      ns = eleNS;
      return parent.att('xmlns:' + prefix, eleNS);
    }
  }
  else {
    return prefix ? parent.ele(prefix + ':' + name) : parent.ele(name);
  }
}

const doc = create({ parser: { element: elementParser } }, obj);
console.log(doc.end({ prettyPrint: true }));
<?xml version="1.0"?>
<root xmlns:ns1="some/uri">
  <ns1:node1/>
  <ns1:node2/>
  <node3/>
</root>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant